1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge branch 'master' into migrate-to-ES6-46

This commit is contained in:
Cameron 2020-08-08 18:42:06 +01:00 committed by GitHub
commit db0c67d9b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1034 additions and 976 deletions

View file

@ -2,6 +2,8 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'apphost',
'use strict';
focusManager = focusManager.default || focusManager;
layoutManager = layoutManager.default || layoutManager;
function onSubmit(e) {
e.preventDefault();
return false;

View file

@ -1,6 +1,7 @@
define(['dialogHelper', 'globalize', 'userSettings', 'layoutManager', 'connectionManager', 'require', 'loading', 'scrollHelper', 'emby-checkbox', 'emby-radio', 'css!./../formdialog', 'material-icons'], function (dialogHelper, globalize, userSettings, layoutManager, connectionManager, require, loading, scrollHelper) {
'use strict';
layoutManager = layoutManager.default || layoutManager;
scrollHelper = scrollHelper.default || scrollHelper;
function saveCategories(context, options) {

View file

@ -4,6 +4,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
playbackManager = playbackManager.default || playbackManager;
browser = browser.default || browser;
loading = loading.default || loading;
layoutManager = layoutManager.default || layoutManager;
focusManager = focusManager.default || focusManager;
scrollHelper = scrollHelper.default || scrollHelper;
serverNotifications = serverNotifications.default || serverNotifications;

View file

@ -1,131 +1,130 @@
define(['playbackManager', 'serverNotifications', 'events'], function (playbackManager, serverNotifications, events) {
'use strict';
import playbackManager from 'playbackManager';
import serverNotifications from 'serverNotifications';
import events from 'events';
serverNotifications = serverNotifications.default || serverNotifications;
playbackManager = playbackManager.default || playbackManager;
function onUserDataChanged(e, apiClient, userData) {
const instance = this;
function onUserDataChanged(e, apiClient, userData) {
var instance = this;
var eventsToMonitor = getEventsToMonitor(instance);
// TODO: Check user data change reason?
if (eventsToMonitor.indexOf('markfavorite') !== -1) {
instance.notifyRefreshNeeded();
} else if (eventsToMonitor.indexOf('markplayed') !== -1) {
instance.notifyRefreshNeeded();
}
}
function getEventsToMonitor(instance) {
var options = instance.options;
var monitor = options ? options.monitorEvents : null;
if (monitor) {
return monitor.split(',');
}
return [];
}
function onTimerCreated(e, apiClient, data) {
var instance = this;
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
function onSeriesTimerCreated(e, apiClient, data) {
var instance = this;
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
function onTimerCancelled(e, apiClient, data) {
var instance = this;
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
function onSeriesTimerCancelled(e, apiClient, data) {
var instance = this;
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
function onLibraryChanged(e, apiClient, data) {
var instance = this;
var eventsToMonitor = getEventsToMonitor(instance);
if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) {
// yes this is an assumption
return;
}
var itemsAdded = data.ItemsAdded || [];
var itemsRemoved = data.ItemsRemoved || [];
if (!itemsAdded.length && !itemsRemoved.length) {
return;
}
var options = instance.options || {};
var parentId = options.parentId;
if (parentId) {
var foldersAddedTo = data.FoldersAddedTo || [];
var foldersRemovedFrom = data.FoldersRemovedFrom || [];
var collectionFolders = data.CollectionFolders || [];
if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) {
return;
}
}
const eventsToMonitor = getEventsToMonitor(instance);
// TODO: Check user data change reason?
if (eventsToMonitor.indexOf('markfavorite') !== -1) {
instance.notifyRefreshNeeded();
} else if (eventsToMonitor.indexOf('markplayed') !== -1) {
instance.notifyRefreshNeeded();
}
}
function onPlaybackStopped(e, stopInfo) {
var instance = this;
function getEventsToMonitor(instance) {
const options = instance.options;
const monitor = options ? options.monitorEvents : null;
if (monitor) {
return monitor.split(',');
}
var state = stopInfo.state;
return [];
}
var eventsToMonitor = getEventsToMonitor(instance);
if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') {
if (eventsToMonitor.indexOf('videoplayback') !== -1) {
instance.notifyRefreshNeeded(true);
return;
}
} else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') {
if (eventsToMonitor.indexOf('audioplayback') !== -1) {
instance.notifyRefreshNeeded(true);
return;
}
function onTimerCreated(e, apiClient, data) {
const instance = this;
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
function onSeriesTimerCreated(e, apiClient, data) {
const instance = this;
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
function onTimerCancelled(e, apiClient, data) {
const instance = this;
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
function onSeriesTimerCancelled(e, apiClient, data) {
const instance = this;
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
function onLibraryChanged(e, apiClient, data) {
const instance = this;
const eventsToMonitor = getEventsToMonitor(instance);
if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) {
// yes this is an assumption
return;
}
const itemsAdded = data.ItemsAdded || [];
const itemsRemoved = data.ItemsRemoved || [];
if (!itemsAdded.length && !itemsRemoved.length) {
return;
}
const options = instance.options || {};
const parentId = options.parentId;
if (parentId) {
const foldersAddedTo = data.FoldersAddedTo || [];
const foldersRemovedFrom = data.FoldersRemovedFrom || [];
const collectionFolders = data.CollectionFolders || [];
if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) {
return;
}
}
function addNotificationEvent(instance, name, handler, owner) {
var localHandler = handler.bind(instance);
instance.notifyRefreshNeeded();
}
function onPlaybackStopped(e, stopInfo) {
const instance = this;
const state = stopInfo.state;
const eventsToMonitor = getEventsToMonitor(instance);
if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') {
if (eventsToMonitor.indexOf('videoplayback') !== -1) {
instance.notifyRefreshNeeded(true);
return;
}
} else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') {
if (eventsToMonitor.indexOf('audioplayback') !== -1) {
instance.notifyRefreshNeeded(true);
return;
}
}
}
function addNotificationEvent(instance, name, handler, owner) {
const localHandler = handler.bind(instance);
owner = owner || serverNotifications;
events.on(owner, name, localHandler);
instance['event_' + name] = localHandler;
}
function removeNotificationEvent(instance, name, owner) {
const handler = instance['event_' + name];
if (handler) {
owner = owner || serverNotifications;
events.on(owner, name, localHandler);
instance['event_' + name] = localHandler;
events.off(owner, name, handler);
instance['event_' + name] = null;
}
}
function removeNotificationEvent(instance, name, owner) {
var handler = instance['event_' + name];
if (handler) {
owner = owner || serverNotifications;
events.off(owner, name, handler);
instance['event_' + name] = null;
}
}
function ItemsRefresher(options) {
class ItemsRefresher {
constructor(options) {
this.options = options || {};
addNotificationEvent(this, 'UserDataChanged', onUserDataChanged);
@ -137,18 +136,18 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
addNotificationEvent(this, 'playbackstop', onPlaybackStopped, playbackManager);
}
ItemsRefresher.prototype.pause = function () {
pause() {
clearRefreshInterval(this, true);
this.paused = true;
};
}
ItemsRefresher.prototype.resume = function (options) {
resume(options) {
this.paused = false;
var refreshIntervalEndTime = this.refreshIntervalEndTime;
const refreshIntervalEndTime = this.refreshIntervalEndTime;
if (refreshIntervalEndTime) {
var remainingMs = refreshIntervalEndTime - new Date().getTime();
const remainingMs = refreshIntervalEndTime - new Date().getTime();
if (remainingMs > 0 && !this.needsRefresh) {
resetRefreshInterval(this, remainingMs);
} else {
@ -162,9 +161,9 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
}
return Promise.resolve();
};
}
ItemsRefresher.prototype.refreshItems = function () {
refreshItems() {
if (!this.fetchData) {
return Promise.resolve();
}
@ -177,15 +176,15 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
this.needsRefresh = false;
return this.fetchData().then(onDataFetched.bind(this));
};
}
ItemsRefresher.prototype.notifyRefreshNeeded = function (isInForeground) {
notifyRefreshNeeded(isInForeground) {
if (this.paused) {
this.needsRefresh = true;
return;
}
var timeout = this.refreshTimeout;
const timeout = this.refreshTimeout;
if (timeout) {
clearTimeout(timeout);
}
@ -195,44 +194,9 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
} else {
this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 10000);
}
};
function clearRefreshInterval(instance, isPausing) {
if (instance.refreshInterval) {
clearInterval(instance.refreshInterval);
instance.refreshInterval = null;
if (!isPausing) {
instance.refreshIntervalEndTime = null;
}
}
}
function resetRefreshInterval(instance, intervalMs) {
clearRefreshInterval(instance);
if (!intervalMs) {
var options = instance.options;
if (options) {
intervalMs = options.refreshIntervalMs;
}
}
if (intervalMs) {
instance.refreshInterval = setInterval(instance.notifyRefreshNeeded.bind(instance), intervalMs);
instance.refreshIntervalEndTime = new Date().getTime() + intervalMs;
}
}
function onDataFetched(result) {
resetRefreshInterval(this);
if (this.afterRefresh) {
this.afterRefresh(result);
}
}
ItemsRefresher.prototype.destroy = function () {
destroy() {
clearRefreshInterval(this);
removeNotificationEvent(this, 'UserDataChanged');
@ -245,7 +209,42 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
this.fetchData = null;
this.options = null;
};
}
}
return ItemsRefresher;
});
function clearRefreshInterval(instance, isPausing) {
if (instance.refreshInterval) {
clearInterval(instance.refreshInterval);
instance.refreshInterval = null;
if (!isPausing) {
instance.refreshIntervalEndTime = null;
}
}
}
function resetRefreshInterval(instance, intervalMs) {
clearRefreshInterval(instance);
if (!intervalMs) {
const options = instance.options;
if (options) {
intervalMs = options.refreshIntervalMs;
}
}
if (intervalMs) {
instance.refreshInterval = setInterval(instance.notifyRefreshNeeded.bind(instance), intervalMs);
instance.refreshIntervalEndTime = new Date().getTime() + intervalMs;
}
}
function onDataFetched(result) {
resetRefreshInterval(this);
if (this.afterRefresh) {
this.afterRefresh(result);
}
}
export default ItemsRefresher;

View file

@ -1,23 +1,19 @@
define(['browser', 'appSettings', 'events'], function (browser, appSettings, events) {
'use strict';
import browser from 'browser';
import appSettings from 'appSettings';
import events from 'events';
browser = browser.default || browser;
function setLayout(instance, layout, selectedLayout) {
if (layout === selectedLayout) {
instance[layout] = true;
document.documentElement.classList.add('layout-' + layout);
} else {
instance[layout] = false;
document.documentElement.classList.remove('layout-' + layout);
}
function setLayout(instance, layout, selectedLayout) {
if (layout === selectedLayout) {
instance[layout] = true;
document.documentElement.classList.add('layout-' + layout);
} else {
instance[layout] = false;
document.documentElement.classList.remove('layout-' + layout);
}
}
function LayoutManager() {
}
LayoutManager.prototype.setLayout = function (layout, save) {
class LayoutManager {
setLayout(layout, save) {
if (!layout || layout === 'auto') {
this.autoLayout();
@ -35,13 +31,13 @@ define(['browser', 'appSettings', 'events'], function (browser, appSettings, eve
}
events.trigger(this, 'modechange');
};
}
LayoutManager.prototype.getSavedLayout = function (layout) {
getSavedLayout(layout) {
return appSettings.get('layout');
};
}
LayoutManager.prototype.autoLayout = function () {
autoLayout() {
// Take a guess at initial layout. The consuming app can override
if (browser.mobile) {
this.setLayout('mobile', false);
@ -50,16 +46,16 @@ define(['browser', 'appSettings', 'events'], function (browser, appSettings, eve
} else {
this.setLayout(this.defaultLayout || 'tv', false);
}
};
}
LayoutManager.prototype.init = function () {
var saved = this.getSavedLayout();
init() {
const saved = this.getSavedLayout();
if (saved) {
this.setLayout(saved, false);
} else {
this.autoLayout();
}
};
}
}
return new LayoutManager();
});
export default new LayoutManager();

View file

@ -1,6 +1,7 @@
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'datetime', 'imageLoader', 'recordingFields', 'events', 'emby-checkbox', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, datetime, imageLoader, recordingFields, events) {
'use strict';
layoutManager = layoutManager.default || layoutManager;
scrollHelper = scrollHelper.default || scrollHelper;
var currentDialog;

View file

@ -3,6 +3,7 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
scrollHelper = scrollHelper.default || scrollHelper;
loading = loading.default || loading;
layoutManager = layoutManager.default || layoutManager;
var currentDialog;
var recordingDeleted = false;

View file

@ -2,6 +2,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
'use strict';
playbackManager = playbackManager.default || playbackManager;
layoutManager = layoutManager.default || layoutManager;
var showMuteButton = true;
var showVolumeSlider = true;

View file

@ -6,6 +6,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
'use strict';
browser = browser.default || browser;
layoutManager = layoutManager.default || layoutManager;
focusManager = focusManager.default || focusManager;
/**

View file

@ -1,6 +1,7 @@
define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager', 'globalize', 'userSettings', 'emby-select', 'paper-icon-button-light', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (require, dom, focusManager, dialogHelper, loading, layoutManager, connectionManager, globalize, userSettings) {
'use strict';
layoutManager = layoutManager.default || layoutManager;
focusManager = focusManager.default || focusManager;
function onSubmit(e) {

View file

@ -1,428 +1,437 @@
define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', 'connectionManager', 'loading', 'focusManager', 'dom', 'apphost', 'emby-select', 'listViewStyle', 'paper-icon-button-light', 'css!./../formdialog', 'material-icons', 'css!./subtitleeditor', 'emby-button', 'flexStyles'], function (dialogHelper, require, layoutManager, globalize, userSettings, connectionManager, loading, focusManager, dom, appHost) {
'use strict';
import dialogHelper from 'dialogHelper';
import layoutManager from 'layoutManager';
import globalize from 'globalize';
import * as userSettings from 'userSettings';
import connectionManager from 'connectionManager';
import loading from 'loading';
import focusManager from 'focusManager';
import dom from 'dom';
import 'emby-select';
import 'listViewStyle';
import 'paper-icon-button-light';
import 'css!./../formdialog';
import 'material-icons';
import 'css!./subtitleeditor';
import 'emby-button';
import 'flexStyles';
loading = loading.default || loading;
focusManager = focusManager.default || focusManager;
let currentItem;
let hasChanges;
var currentItem;
var hasChanges;
function downloadRemoteSubtitles(context, id) {
let url = 'Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + id;
function downloadRemoteSubtitles(context, id) {
var url = 'Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + id;
let apiClient = connectionManager.getApiClient(currentItem.ServerId);
apiClient.ajax({
var apiClient = connectionManager.getApiClient(currentItem.ServerId);
apiClient.ajax({
type: 'POST',
url: apiClient.getUrl(url)
type: 'POST',
url: apiClient.getUrl(url)
}).then(function () {
hasChanges = true;
import('toast').then(({default: toast}) => {
toast(globalize.translate('MessageDownloadQueued'));
});
focusManager.autoFocus(context);
});
}
function deleteLocalSubtitle(context, index) {
let msg = globalize.translate('MessageAreYouSureDeleteSubtitles');
import('confirm').then(({default: confirm}) => {
confirm({
title: globalize.translate('ConfirmDeletion'),
text: msg,
confirmText: globalize.translate('Delete'),
primary: 'delete'
}).then(function () {
hasChanges = true;
loading.show();
require(['toast'], function (toast) {
toast(globalize.translate('MessageDownloadQueued'));
});
let itemId = currentItem.Id;
let url = 'Videos/' + itemId + '/Subtitles/' + index;
focusManager.autoFocus(context);
});
}
let apiClient = connectionManager.getApiClient(currentItem.ServerId);
function deleteLocalSubtitle(context, index) {
var msg = globalize.translate('MessageAreYouSureDeleteSubtitles');
apiClient.ajax({
require(['confirm'], function (confirm) {
confirm.default({
title: globalize.translate('ConfirmDeletion'),
text: msg,
confirmText: globalize.translate('Delete'),
primary: 'delete'
type: 'DELETE',
url: apiClient.getUrl(url)
}).then(function () {
loading.show();
var itemId = currentItem.Id;
var url = 'Videos/' + itemId + '/Subtitles/' + index;
var apiClient = connectionManager.getApiClient(currentItem.ServerId);
apiClient.ajax({
type: 'DELETE',
url: apiClient.getUrl(url)
}).then(function () {
hasChanges = true;
reload(context, apiClient, itemId);
});
hasChanges = true;
reload(context, apiClient, itemId);
});
});
}
});
}
function fillSubtitleList(context, item) {
var streams = item.MediaStreams || [];
function fillSubtitleList(context, item) {
let streams = item.MediaStreams || [];
var subs = streams.filter(function (s) {
return s.Type === 'Subtitle';
});
let subs = streams.filter(function (s) {
return s.Type === 'Subtitle';
});
var html = '';
let html = '';
if (subs.length) {
html += '<h2>' + globalize.translate('MySubtitles') + '</h2>';
if (subs.length) {
html += '<h2>' + globalize.translate('MySubtitles') + '</h2>';
html += '<div>';
html += '<div>';
html += subs.map(function (s) {
var itemHtml = '';
html += subs.map(function (s) {
let itemHtml = '';
var tagName = layoutManager.tv ? 'button' : 'div';
var className = layoutManager.tv && s.Path ? 'listItem listItem-border btnDelete' : 'listItem listItem-border';
let tagName = layoutManager.tv ? 'button' : 'div';
let className = layoutManager.tv && s.Path ? 'listItem listItem-border btnDelete' : 'listItem listItem-border';
if (layoutManager.tv) {
className += ' listItem-focusscale listItem-button';
}
className += ' listItem-noborder';
itemHtml += '<' + tagName + ' class="' + className + '" data-index="' + s.Index + '">';
itemHtml += '<span class="listItemIcon material-icons closed_caption"></span>';
itemHtml += '<div class="listItemBody two-line">';
itemHtml += '<div>';
itemHtml += s.DisplayTitle || '';
itemHtml += '</div>';
if (s.Path) {
itemHtml += '<div class="secondary listItemBodyText">' + (s.Path) + '</div>';
}
itemHtml += '</a>';
itemHtml += '</div>';
if (!layoutManager.tv) {
if (s.Path) {
itemHtml += '<button is="paper-icon-button-light" data-index="' + s.Index + '" title="' + globalize.translate('Delete') + '" class="btnDelete listItemButton"><span class="material-icons delete"></span></button>';
}
}
itemHtml += '</' + tagName + '>';
return itemHtml;
}).join('');
html += '</div>';
}
var elem = context.querySelector('.subtitleList');
if (subs.length) {
elem.classList.remove('hide');
} else {
elem.classList.add('hide');
}
elem.innerHTML = html;
}
function fillLanguages(context, apiClient, languages) {
var selectLanguage = context.querySelector('#selectLanguage');
selectLanguage.innerHTML = languages.map(function (l) {
return '<option value="' + l.ThreeLetterISOLanguageName + '">' + l.DisplayName + '</option>';
});
var lastLanguage = userSettings.get('subtitleeditor-language');
if (lastLanguage) {
selectLanguage.value = lastLanguage;
} else {
apiClient.getCurrentUser().then(function (user) {
var lang = user.Configuration.SubtitleLanguagePreference;
if (lang) {
selectLanguage.value = lang;
}
});
}
}
function renderSearchResults(context, results) {
var lastProvider = '';
var html = '';
if (!results.length) {
context.querySelector('.noSearchResults').classList.remove('hide');
context.querySelector('.subtitleResults').innerHTML = '';
loading.hide();
return;
}
context.querySelector('.noSearchResults').classList.add('hide');
for (var i = 0, length = results.length; i < length; i++) {
var result = results[i];
var provider = result.ProviderName;
if (provider !== lastProvider) {
if (i > 0) {
html += '</div>';
}
html += '<h2>' + provider + '</h2>';
html += '<div>';
lastProvider = provider;
}
var tagName = layoutManager.tv ? 'button' : 'div';
var className = layoutManager.tv ? 'listItem listItem-border btnOptions' : 'listItem listItem-border';
if (layoutManager.tv) {
className += ' listItem-focusscale listItem-button';
}
html += '<' + tagName + ' class="' + className + '" data-subid="' + result.Id + '">';
className += ' listItem-noborder';
html += '<span class="listItemIcon material-icons closed_caption"></span>';
itemHtml += '<' + tagName + ' class="' + className + '" data-index="' + s.Index + '">';
var bodyClass = result.Comment || result.IsHashMatch ? 'three-line' : 'two-line';
itemHtml += '<span class="listItemIcon material-icons closed_caption"></span>';
html += '<div class="listItemBody ' + bodyClass + '">';
itemHtml += '<div class="listItemBody two-line">';
html += '<div>' + (result.Name) + '</div>';
html += '<div class="secondary listItemBodyText">';
itemHtml += '<div>';
itemHtml += s.DisplayTitle || '';
itemHtml += '</div>';
if (result.Format) {
html += '<span style="margin-right:1em;">' + globalize.translate('FormatValue', result.Format) + '</span>';
if (s.Path) {
itemHtml += '<div class="secondary listItemBodyText">' + (s.Path) + '</div>';
}
if (result.DownloadCount != null) {
html += '<span>' + globalize.translate('DownloadsValue', result.DownloadCount) + '</span>';
}
html += '</div>';
if (result.Comment) {
html += '<div class="secondary listItemBodyText">' + (result.Comment) + '</div>';
}
if (result.IsHashMatch) {
html += '<div class="secondary listItemBodyText"><div class="inline-flex align-items-center justify-content-center" style="background:#3388cc;color:#fff;padding: .3em 1em;border-radius:1000em;">' + globalize.translate('PerfectMatch') + '</div></div>';
}
html += '</div>';
itemHtml += '</a>';
itemHtml += '</div>';
if (!layoutManager.tv) {
html += '<button type="button" is="paper-icon-button-light" data-subid="' + result.Id + '" class="btnDownload listItemButton"><span class="material-icons file_download"></span></button>';
if (s.Path) {
itemHtml += '<button is="paper-icon-button-light" data-index="' + s.Index + '" title="' + globalize.translate('Delete') + '" class="btnDelete listItemButton"><span class="material-icons delete"></span></button>';
}
}
html += '</' + tagName + '>';
itemHtml += '</' + tagName + '>';
return itemHtml;
}).join('');
html += '</div>';
}
let elem = context.querySelector('.subtitleList');
if (subs.length) {
elem.classList.remove('hide');
} else {
elem.classList.add('hide');
}
elem.innerHTML = html;
}
function fillLanguages(context, apiClient, languages) {
let selectLanguage = context.querySelector('#selectLanguage');
selectLanguage.innerHTML = languages.map(function (l) {
return '<option value="' + l.ThreeLetterISOLanguageName + '">' + l.DisplayName + '</option>';
});
let lastLanguage = userSettings.get('subtitleeditor-language');
if (lastLanguage) {
selectLanguage.value = lastLanguage;
} else {
apiClient.getCurrentUser().then(function (user) {
let lang = user.Configuration.SubtitleLanguagePreference;
if (lang) {
selectLanguage.value = lang;
}
});
}
}
function renderSearchResults(context, results) {
let lastProvider = '';
let html = '';
if (!results.length) {
context.querySelector('.noSearchResults').classList.remove('hide');
context.querySelector('.subtitleResults').innerHTML = '';
loading.hide();
return;
}
context.querySelector('.noSearchResults').classList.add('hide');
for (let i = 0, length = results.length; i < length; i++) {
let result = results[i];
let provider = result.ProviderName;
if (provider !== lastProvider) {
if (i > 0) {
html += '</div>';
}
html += '<h2>' + provider + '</h2>';
html += '<div>';
lastProvider = provider;
}
if (results.length) {
html += '</div>';
let tagName = layoutManager.tv ? 'button' : 'div';
let className = layoutManager.tv ? 'listItem listItem-border btnOptions' : 'listItem listItem-border';
if (layoutManager.tv) {
className += ' listItem-focusscale listItem-button';
}
var elem = context.querySelector('.subtitleResults');
elem.innerHTML = html;
html += '<' + tagName + ' class="' + className + '" data-subid="' + result.Id + '">';
html += '<span class="listItemIcon material-icons closed_caption"></span>';
let bodyClass = result.Comment || result.IsHashMatch ? 'three-line' : 'two-line';
html += '<div class="listItemBody ' + bodyClass + '">';
html += '<div>' + (result.Name) + '</div>';
html += '<div class="secondary listItemBodyText">';
if (result.Format) {
html += '<span style="margin-right:1em;">' + globalize.translate('FormatValue', result.Format) + '</span>';
}
if (result.DownloadCount != null) {
html += '<span>' + globalize.translate('DownloadsValue', result.DownloadCount) + '</span>';
}
html += '</div>';
if (result.Comment) {
html += '<div class="secondary listItemBodyText">' + (result.Comment) + '</div>';
}
if (result.IsHashMatch) {
html += '<div class="secondary listItemBodyText"><div class="inline-flex align-items-center justify-content-center" style="background:#3388cc;color:#fff;padding: .3em 1em;border-radius:1000em;">' + globalize.translate('PerfectMatch') + '</div></div>';
}
html += '</div>';
if (!layoutManager.tv) {
html += '<button type="button" is="paper-icon-button-light" data-subid="' + result.Id + '" class="btnDownload listItemButton"><span class="material-icons file_download"></span></button>';
}
html += '</' + tagName + '>';
}
if (results.length) {
html += '</div>';
}
let elem = context.querySelector('.subtitleResults');
elem.innerHTML = html;
loading.hide();
}
function searchForSubtitles(context, language) {
userSettings.set('subtitleeditor-language', language);
loading.show();
let apiClient = connectionManager.getApiClient(currentItem.ServerId);
let url = apiClient.getUrl('Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + language);
apiClient.getJSON(url).then(function (results) {
renderSearchResults(context, results);
});
}
function reload(context, apiClient, itemId) {
context.querySelector('.noSearchResults').classList.add('hide');
function onGetItem(item) {
currentItem = item;
fillSubtitleList(context, item);
let file = item.Path || '';
let index = Math.max(file.lastIndexOf('/'), file.lastIndexOf('\\'));
if (index > -1) {
file = file.substring(index + 1);
}
if (file) {
context.querySelector('.pathValue').innerHTML = file;
context.querySelector('.originalFile').classList.remove('hide');
} else {
context.querySelector('.pathValue').innerHTML = '';
context.querySelector('.originalFile').classList.add('hide');
}
loading.hide();
}
function searchForSubtitles(context, language) {
userSettings.set('subtitleeditor-language', language);
if (typeof itemId === 'string') {
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(onGetItem);
} else {
onGetItem(itemId);
}
}
loading.show();
function onSearchSubmit(e) {
let form = this;
var apiClient = connectionManager.getApiClient(currentItem.ServerId);
var url = apiClient.getUrl('Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + language);
let lang = form.querySelector('#selectLanguage', form).value;
apiClient.getJSON(url).then(function (results) {
renderSearchResults(context, results);
});
searchForSubtitles(dom.parentWithClass(form, 'formDialogContent'), lang);
e.preventDefault();
return false;
}
function onSubtitleListClick(e) {
let btnDelete = dom.parentWithClass(e.target, 'btnDelete');
if (btnDelete) {
let index = btnDelete.getAttribute('data-index');
let context = dom.parentWithClass(btnDelete, 'subtitleEditorDialog');
deleteLocalSubtitle(context, index);
}
}
function onSubtitleResultsClick(e) {
let subtitleId;
let context;
let btnOptions = dom.parentWithClass(e.target, 'btnOptions');
if (btnOptions) {
subtitleId = btnOptions.getAttribute('data-subid');
context = dom.parentWithClass(btnOptions, 'subtitleEditorDialog');
showDownloadOptions(btnOptions, context, subtitleId);
}
function reload(context, apiClient, itemId) {
context.querySelector('.noSearchResults').classList.add('hide');
let btnDownload = dom.parentWithClass(e.target, 'btnDownload');
if (btnDownload) {
subtitleId = btnDownload.getAttribute('data-subid');
context = dom.parentWithClass(btnDownload, 'subtitleEditorDialog');
downloadRemoteSubtitles(context, subtitleId);
}
}
function onGetItem(item) {
currentItem = item;
function showDownloadOptions(button, context, subtitleId) {
let items = [];
fillSubtitleList(context, item);
var file = item.Path || '';
var index = Math.max(file.lastIndexOf('/'), file.lastIndexOf('\\'));
if (index > -1) {
file = file.substring(index + 1);
items.push({
name: globalize.translate('Download'),
id: 'download'
});
import('actionsheet').then(({default: actionsheet}) => {
actionsheet.show({
items: items,
positionTo: button
}).then(function (id) {
switch (id) {
case 'download':
downloadRemoteSubtitles(context, subtitleId);
break;
default:
break;
}
});
});
}
if (file) {
context.querySelector('.pathValue').innerHTML = file;
context.querySelector('.originalFile').classList.remove('hide');
} else {
context.querySelector('.pathValue').innerHTML = '';
context.querySelector('.originalFile').classList.add('hide');
}
function centerFocus(elem, horiz, on) {
import('scrollHelper').then(({default: scrollHelper}) => {
let fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});
}
loading.hide();
}
function showEditorInternal(itemId, serverId, template) {
hasChanges = false;
if (typeof itemId === 'string') {
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(onGetItem);
let apiClient = connectionManager.getApiClient(serverId);
return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
let dialogOptions = {
removeOnClose: true,
scrollY: false
};
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
onGetItem(itemId);
}
}
function onSearchSubmit(e) {
var form = this;
var lang = form.querySelector('#selectLanguage', form).value;
searchForSubtitles(dom.parentWithClass(form, 'formDialogContent'), lang);
e.preventDefault();
return false;
}
function onSubtitleListClick(e) {
var btnDelete = dom.parentWithClass(e.target, 'btnDelete');
if (btnDelete) {
var index = btnDelete.getAttribute('data-index');
var context = dom.parentWithClass(btnDelete, 'subtitleEditorDialog');
deleteLocalSubtitle(context, index);
}
}
function onSubtitleResultsClick(e) {
var subtitleId;
var context;
var btnOptions = dom.parentWithClass(e.target, 'btnOptions');
if (btnOptions) {
subtitleId = btnOptions.getAttribute('data-subid');
context = dom.parentWithClass(btnOptions, 'subtitleEditorDialog');
showDownloadOptions(btnOptions, context, subtitleId);
dialogOptions.size = 'small';
}
var btnDownload = dom.parentWithClass(e.target, 'btnDownload');
if (btnDownload) {
subtitleId = btnDownload.getAttribute('data-subid');
context = dom.parentWithClass(btnDownload, 'subtitleEditorDialog');
downloadRemoteSubtitles(context, subtitleId);
let dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
dlg.classList.add('subtitleEditorDialog');
dlg.innerHTML = globalize.translateHtml(template, 'core');
dlg.querySelector('.originalSubtitleFileLabel').innerHTML = globalize.translate('File');
dlg.querySelector('.subtitleSearchForm').addEventListener('submit', onSearchSubmit);
let btnSubmit = dlg.querySelector('.btnSubmit');
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, true);
dlg.querySelector('.btnSearchSubtitles').classList.add('hide');
} else {
btnSubmit.classList.add('hide');
}
}
function showDownloadOptions(button, context, subtitleId) {
var items = [];
let editorContent = dlg.querySelector('.formDialogContent');
items.push({
name: globalize.translate('Download'),
id: 'download'
dlg.querySelector('.subtitleList').addEventListener('click', onSubtitleListClick);
dlg.querySelector('.subtitleResults').addEventListener('click', onSubtitleResultsClick);
apiClient.getCultures().then(function (languages) {
fillLanguages(editorContent, apiClient, languages);
});
require(['actionsheet'], function (actionsheet) {
actionsheet.show({
items: items,
positionTo: button
}).then(function (id) {
switch (id) {
case 'download':
downloadRemoteSubtitles(context, subtitleId);
break;
default:
break;
}
});
dlg.querySelector('.btnCancel').addEventListener('click', function () {
dialogHelper.close(dlg);
});
}
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
scrollHelper = scrollHelper.default || scrollHelper;
var fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});
}
function showEditorInternal(itemId, serverId, template) {
hasChanges = false;
var apiClient = connectionManager.getApiClient(serverId);
return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
var dialogOptions = {
removeOnClose: true,
scrollY: false
};
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
dlg.classList.add('subtitleEditorDialog');
dlg.innerHTML = globalize.translateHtml(template, 'core');
dlg.querySelector('.originalSubtitleFileLabel').innerHTML = globalize.translate('File');
dlg.querySelector('.subtitleSearchForm').addEventListener('submit', onSearchSubmit);
var btnSubmit = dlg.querySelector('.btnSubmit');
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, true);
dlg.querySelector('.btnSearchSubtitles').classList.add('hide');
} else {
btnSubmit.classList.add('hide');
}
var editorContent = dlg.querySelector('.formDialogContent');
dlg.querySelector('.subtitleList').addEventListener('click', onSubtitleListClick);
dlg.querySelector('.subtitleResults').addEventListener('click', onSubtitleResultsClick);
apiClient.getCultures().then(function (languages) {
fillLanguages(editorContent, apiClient, languages);
});
dlg.querySelector('.btnCancel').addEventListener('click', function () {
dialogHelper.close(dlg);
});
return new Promise(function (resolve, reject) {
dlg.addEventListener('close', function () {
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, false);
}
if (hasChanges) {
resolve();
} else {
reject();
}
});
dialogHelper.open(dlg);
reload(editorContent, apiClient, item);
});
});
}
function showEditor(itemId, serverId) {
loading.show();
return new Promise(function (resolve, reject) {
require(['text!./subtitleeditor.template.html'], function (template) {
showEditorInternal(itemId, serverId, template).then(resolve, reject);
});
});
}
dlg.addEventListener('close', function () {
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, false);
}
return {
show: showEditor
};
});
if (hasChanges) {
resolve();
} else {
reject();
}
});
dialogHelper.open(dlg);
reload(editorContent, apiClient, item);
});
});
}
function showEditor(itemId, serverId) {
loading.show();
return new Promise(function (resolve, reject) {
import('text!./subtitleeditor.template.html').then(({default: template}) => {
showEditorInternal(itemId, serverId, template).then(resolve, reject);
});
});
}
export default {
show: showEditor
};

View file

@ -1,147 +1,148 @@
define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', 'css!./subtitlesync'], function (playbackManager, layoutManager, template, css) {
'use strict';
import playbackManager from 'playbackManager';
import layoutManager from 'layoutManager';
import template from 'text!./subtitlesync.template.html';
import 'css!./subtitlesync';
playbackManager = playbackManager.default || playbackManager;
let player;
let subtitleSyncSlider;
let subtitleSyncTextField;
let subtitleSyncCloseButton;
let subtitleSyncContainer;
var player;
var subtitleSyncSlider;
var subtitleSyncTextField;
var subtitleSyncCloseButton;
var subtitleSyncContainer;
function init(instance) {
const parent = document.createElement('div');
document.body.appendChild(parent);
parent.innerHTML = template;
function init(instance) {
var parent = document.createElement('div');
document.body.appendChild(parent);
parent.innerHTML = template;
subtitleSyncSlider = parent.querySelector('.subtitleSyncSlider');
subtitleSyncTextField = parent.querySelector('.subtitleSyncTextField');
subtitleSyncCloseButton = parent.querySelector('.subtitleSync-closeButton');
subtitleSyncContainer = parent.querySelector('.subtitleSyncContainer');
subtitleSyncSlider = parent.querySelector('.subtitleSyncSlider');
subtitleSyncTextField = parent.querySelector('.subtitleSyncTextField');
subtitleSyncCloseButton = parent.querySelector('.subtitleSync-closeButton');
subtitleSyncContainer = parent.querySelector('.subtitleSyncContainer');
if (layoutManager.tv) {
subtitleSyncSlider.classList.add('focusable');
// HACK: Delay to give time for registered element attach (Firefox)
setTimeout(function () {
subtitleSyncSlider.enableKeyboardDragging();
}, 0);
}
if (layoutManager.tv) {
subtitleSyncSlider.classList.add('focusable');
// HACK: Delay to give time for registered element attach (Firefox)
setTimeout(function () {
subtitleSyncSlider.enableKeyboardDragging();
}, 0);
}
subtitleSyncContainer.classList.add('hide');
subtitleSyncContainer.classList.add('hide');
subtitleSyncTextField.updateOffset = function (offset) {
this.textContent = offset + 's';
};
subtitleSyncTextField.updateOffset = function(offset) {
this.textContent = offset + 's';
};
subtitleSyncTextField.addEventListener('click', function () {
// keep focus to prevent fade with osd
this.hasFocus = true;
});
subtitleSyncTextField.addEventListener('click', function () {
subtitleSyncTextField.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
// if input key is enter search for float pattern
let inputOffset = /[-+]?\d+\.?\d*/g.exec(this.textContent);
if (inputOffset) {
inputOffset = inputOffset[0];
// replace current text by considered offset
this.textContent = inputOffset + 's';
inputOffset = parseFloat(inputOffset);
// set new offset
playbackManager.setSubtitleOffset(inputOffset, player);
// synchronize with slider value
subtitleSyncSlider.updateOffset(
getPercentageFromOffset(inputOffset));
} else {
this.textContent = (playbackManager.getPlayerSubtitleOffset(player) || 0) + 's';
}
this.hasFocus = false;
event.preventDefault();
} else {
// keep focus to prevent fade with osd
this.hasFocus = true;
});
subtitleSyncTextField.addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
// if input key is enter search for float pattern
var inputOffset = /[-+]?\d+\.?\d*/g.exec(this.textContent);
if (inputOffset) {
inputOffset = inputOffset[0];
// replace current text by considered offset
this.textContent = inputOffset + 's';
inputOffset = parseFloat(inputOffset);
// set new offset
playbackManager.setSubtitleOffset(inputOffset, player);
// synchronize with slider value
subtitleSyncSlider.updateOffset(
getPercentageFromOffset(inputOffset));
} else {
this.textContent = (playbackManager.getPlayerSubtitleOffset(player) || 0) + 's';
}
this.hasFocus = false;
if (event.key.match(/[+-\d.s]/) === null) {
event.preventDefault();
} else {
// keep focus to prevent fade with osd
this.hasFocus = true;
if (event.key.match(/[+-\d.s]/) === null) {
event.preventDefault();
}
}
}
// FIXME: TV layout will require special handling for navigation keys. But now field is not focusable
event.stopPropagation();
});
// FIXME: TV layout will require special handling for navigation keys. But now field is not focusable
event.stopPropagation();
});
subtitleSyncTextField.blur = function() {
// prevent textfield to blur while element has focus
if (!this.hasFocus && this.prototype) {
this.prototype.blur();
}
};
subtitleSyncTextField.blur = function () {
// prevent textfield to blur while element has focus
if (!this.hasFocus && this.prototype) {
this.prototype.blur();
}
};
subtitleSyncSlider.updateOffset = function(percent) {
// default value is 0s = 50%
this.value = percent === undefined ? 50 : percent;
};
subtitleSyncSlider.updateOffset = function (percent) {
// default value is 0s = 50%
this.value = percent === undefined ? 50 : percent;
};
subtitleSyncSlider.addEventListener('change', function () {
// set new offset
playbackManager.setSubtitleOffset(getOffsetFromPercentage(this.value), player);
// synchronize with textField value
subtitleSyncTextField.updateOffset(
getOffsetFromPercentage(this.value));
});
subtitleSyncSlider.addEventListener('change', function () {
// set new offset
playbackManager.setSubtitleOffset(getOffsetFromPercentage(this.value), player);
// synchronize with textField value
subtitleSyncTextField.updateOffset(
getOffsetFromPercentage(this.value));
});
subtitleSyncSlider.getBubbleHtml = function (value) {
var newOffset = getOffsetFromPercentage(value);
return '<h1 class="sliderBubbleText">' +
subtitleSyncSlider.getBubbleHtml = function (value) {
const newOffset = getOffsetFromPercentage(value);
return '<h1 class="sliderBubbleText">' +
(newOffset > 0 ? '+' : '') + parseFloat(newOffset) + 's' +
'</h1>';
};
};
subtitleSyncCloseButton.addEventListener('click', function() {
playbackManager.disableShowingSubtitleOffset(player);
SubtitleSync.prototype.toggle('forceToHide');
});
subtitleSyncCloseButton.addEventListener('click', function () {
playbackManager.disableShowingSubtitleOffset(player);
SubtitleSync.prototype.toggle('forceToHide');
});
instance.element = parent;
}
instance.element = parent;
}
function getOffsetFromPercentage(value) {
// convert percent to fraction
var offset = (value - 50) / 50;
// multiply by offset min/max range value (-x to +x) :
offset *= 30;
return offset.toFixed(1);
}
function getOffsetFromPercentage(value) {
// convert percent to fraction
let offset = (value - 50) / 50;
// multiply by offset min/max range value (-x to +x) :
offset *= 30;
return offset.toFixed(1);
}
function getPercentageFromOffset(value) {
// divide by offset min/max range value (-x to +x) :
var percentValue = value / 30;
// convert fraction to percent
percentValue *= 50;
percentValue += 50;
return Math.min(100, Math.max(0, percentValue.toFixed()));
}
function getPercentageFromOffset(value) {
// divide by offset min/max range value (-x to +x) :
let percentValue = value / 30;
// convert fraction to percent
percentValue *= 50;
percentValue += 50;
return Math.min(100, Math.max(0, percentValue.toFixed()));
}
function SubtitleSync(currentPlayer) {
class SubtitleSync {
constructor(currentPlayer) {
player = currentPlayer;
init(this);
}
SubtitleSync.prototype.destroy = function() {
destroy() {
SubtitleSync.prototype.toggle('forceToHide');
if (player) {
playbackManager.disableShowingSubtitleOffset(player);
playbackManager.setSubtitleOffset(0, player);
}
var elem = this.element;
const elem = this.element;
if (elem) {
elem.parentNode.removeChild(elem);
this.element = null;
}
};
}
SubtitleSync.prototype.toggle = function(action) {
toggle(action) {
if (player && playbackManager.supportSubtitleOffset(player)) {
/* eslint-disable no-fallthrough */
switch (action) {
@ -170,7 +171,7 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html',
}
/* eslint-enable no-fallthrough */
}
};
}
}
return SubtitleSync;
});
export default SubtitleSync;

View file

@ -3,6 +3,7 @@ define(['dialogHelper', 'dom', 'layoutManager', 'connectionManager', 'globalize'
browser = browser.default || browser;
loading = loading.default || loading;
layoutManager = layoutManager.default || layoutManager;
focusManager = focusManager.default || focusManager;
scrollHelper = scrollHelper.default || scrollHelper;

View file

@ -1,6 +1,8 @@
define(['require', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'userSettings', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (require, dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize, userSettings) {
'use strict';
layoutManager = layoutManager.default || layoutManager;
function onSubmit(e) {
e.preventDefault();
return false;