MOBILE-3773 video: Fix placeholder displayed if no poster
parent
4a07245455
commit
d1bd392a68
|
@ -266,9 +266,11 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
|
||||||
finalUrl = CoreFile.convertFileSrc(finalUrl);
|
finalUrl = CoreFile.convertFileSrc(finalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CoreUrlUtils.isLocalFileUrl(finalUrl)) {
|
if (!CoreUrlUtils.isLocalFileUrl(finalUrl) && !finalUrl.includes('#')) {
|
||||||
/* In iOS, if we use the same URL in embedded file and background download then the download only
|
/* In iOS, if we use the same URL in embedded file and background download then the download only
|
||||||
downloads a few bytes (cached ones). Add a hash to the URL so both URLs are different. */
|
downloads a few bytes (cached ones). Add an anchor to the URL so both URLs are different.
|
||||||
|
Don't add this anchor if the URL already has an anchor, otherwise other anchors might not work.
|
||||||
|
The downloaded URL won't have anchors so the URLs will already be different. */
|
||||||
finalUrl = finalUrl + '#moodlemobile-embedded';
|
finalUrl = finalUrl + '#moodlemobile-embedded';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -555,7 +555,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
});
|
});
|
||||||
|
|
||||||
videos.forEach((video) => {
|
videos.forEach((video) => {
|
||||||
this.treatMedia(video);
|
this.treatMedia(video, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
iframes.forEach((iframe) => {
|
iframes.forEach((iframe) => {
|
||||||
|
@ -673,8 +673,9 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
* Add media adapt class and apply CoreExternalContentDirective to the media element and its sources and tracks.
|
* Add media adapt class and apply CoreExternalContentDirective to the media element and its sources and tracks.
|
||||||
*
|
*
|
||||||
* @param element Video or audio to treat.
|
* @param element Video or audio to treat.
|
||||||
|
* @param isVideo Whether it's a video.
|
||||||
*/
|
*/
|
||||||
protected treatMedia(element: HTMLElement): void {
|
protected treatMedia(element: HTMLElement, isVideo: boolean = false): void {
|
||||||
this.addMediaAdaptClass(element);
|
this.addMediaAdaptClass(element);
|
||||||
this.addExternalContent(element);
|
this.addExternalContent(element);
|
||||||
|
|
||||||
|
@ -692,8 +693,16 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
|
|
||||||
const sources = Array.from(element.querySelectorAll('source'));
|
const sources = Array.from(element.querySelectorAll('source'));
|
||||||
const tracks = Array.from(element.querySelectorAll('track'));
|
const tracks = Array.from(element.querySelectorAll('track'));
|
||||||
|
const hasPoster = isVideo && !!element.getAttribute('poster');
|
||||||
|
|
||||||
|
if (isVideo && !hasPoster) {
|
||||||
|
this.fixVideoSrcPlaceholder(element);
|
||||||
|
}
|
||||||
|
|
||||||
sources.forEach((source) => {
|
sources.forEach((source) => {
|
||||||
|
if (isVideo && !hasPoster) {
|
||||||
|
this.fixVideoSrcPlaceholder(source);
|
||||||
|
}
|
||||||
source.setAttribute('target-src', source.getAttribute('src') || '');
|
source.setAttribute('target-src', source.getAttribute('src') || '');
|
||||||
source.removeAttribute('src');
|
source.removeAttribute('src');
|
||||||
this.addExternalContent(source);
|
this.addExternalContent(source);
|
||||||
|
@ -709,6 +718,24 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to fix the placeholder displayed when a video doesn't have a poster.
|
||||||
|
*
|
||||||
|
* @param element Element to fix.
|
||||||
|
*/
|
||||||
|
protected fixVideoSrcPlaceholder(element: HTMLElement): void {
|
||||||
|
const src = element.getAttribute('src');
|
||||||
|
if (!src) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src.match(/#t=\d/)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
element.setAttribute('src', src + '#t=0.001');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add media adapt class and treat the iframe source.
|
* Add media adapt class and treat the iframe source.
|
||||||
*
|
*
|
||||||
|
|
|
@ -47,6 +47,7 @@ import {
|
||||||
CoreFilepoolQueueDBEntry,
|
CoreFilepoolQueueDBEntry,
|
||||||
} from '@services/database/filepool';
|
} from '@services/database/filepool';
|
||||||
import { CoreFileHelper } from './file-helper';
|
import { CoreFileHelper } from './file-helper';
|
||||||
|
import { CoreUrl } from '@singletons/url';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Factory for handling downloading files and retrieve downloaded files.
|
* Factory for handling downloading files and retrieve downloaded files.
|
||||||
|
@ -672,6 +673,13 @@ export class CoreFilepoolProvider {
|
||||||
poolFileObject?: CoreFilepoolFileEntry,
|
poolFileObject?: CoreFilepoolFileEntry,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const fileId = this.getFileIdByUrl(fileUrl);
|
const fileId = this.getFileIdByUrl(fileUrl);
|
||||||
|
|
||||||
|
// Extract the anchor from the URL (if any).
|
||||||
|
const anchor = CoreUrl.getUrlAnchor(fileUrl);
|
||||||
|
if (anchor) {
|
||||||
|
fileUrl = fileUrl.replace(anchor, '');
|
||||||
|
}
|
||||||
|
|
||||||
const extension = CoreMimetypeUtils.guessExtensionFromUrl(fileUrl);
|
const extension = CoreMimetypeUtils.guessExtensionFromUrl(fileUrl);
|
||||||
const addExtension = typeof filePath == 'undefined';
|
const addExtension = typeof filePath == 'undefined';
|
||||||
const path = filePath || (await this.getFilePath(siteId, fileId, extension));
|
const path = filePath || (await this.getFilePath(siteId, fileId, extension));
|
||||||
|
@ -712,7 +720,8 @@ export class CoreFilepoolProvider {
|
||||||
extension: fileEntry.extension,
|
extension: fileEntry.extension,
|
||||||
});
|
});
|
||||||
|
|
||||||
return fileEntry.toURL();
|
// Add the anchor again to the local URL.
|
||||||
|
return fileEntry.toURL() + (anchor || '');
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
// Download finished, delete the promise.
|
// Download finished, delete the promise.
|
||||||
delete this.filePromises[siteId][downloadId];
|
delete this.filePromises[siteId][downloadId];
|
||||||
|
@ -1015,7 +1024,10 @@ export class CoreFilepoolProvider {
|
||||||
url = await this.getInternalUrlById(siteId, fileId);
|
url = await this.getInternalUrlById(siteId, fileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return finishSuccessfulDownload(url);
|
// Add the anchor to the local URL if any.
|
||||||
|
const anchor = CoreUrl.getUrlAnchor(fileUrl);
|
||||||
|
|
||||||
|
return finishSuccessfulDownload(url + (anchor || ''));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// The file is not downloaded or it's outdated.
|
// The file is not downloaded or it's outdated.
|
||||||
this.notifyFileDownloading(siteId, fileId, links);
|
this.notifyFileDownloading(siteId, fileId, links);
|
||||||
|
@ -1284,6 +1296,9 @@ export class CoreFilepoolProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the anchor.
|
||||||
|
url = CoreUrl.removeUrlAnchor(url);
|
||||||
|
|
||||||
// Try to guess the filename the target file should have.
|
// Try to guess the filename the target file should have.
|
||||||
// We want to keep the original file name so people can easily identify the files after the download.
|
// We want to keep the original file name so people can easily identify the files after the download.
|
||||||
const filename = this.guessFilenameFromUrl(url);
|
const filename = this.guessFilenameFromUrl(url);
|
||||||
|
@ -1446,7 +1461,7 @@ export class CoreFilepoolProvider {
|
||||||
return CoreConstants.NOT_DOWNLOADABLE;
|
return CoreConstants.NOT_DOWNLOADABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileUrl = CoreFileHelper.getFileUrl(file);
|
fileUrl = CoreUrl.removeUrlAnchor(CoreFileHelper.getFileUrl(file));
|
||||||
timemodified = file.timemodified || timemodified;
|
timemodified = file.timemodified || timemodified;
|
||||||
revision = revision || this.getRevisionFromUrl(fileUrl);
|
revision = revision || this.getRevisionFromUrl(fileUrl);
|
||||||
const fileId = this.getFileIdByUrl(fileUrl);
|
const fileId = this.getFileIdByUrl(fileUrl);
|
||||||
|
@ -1556,11 +1571,14 @@ export class CoreFilepoolProvider {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// We found the file entry, now look for the file on disk.
|
// We found the file entry, now look for the file on disk.
|
||||||
if (mode === 'src') {
|
const path = mode === 'src' ?
|
||||||
return await this.getInternalSrcById(siteId, fileId);
|
await this.getInternalSrcById(siteId, fileId) :
|
||||||
} else {
|
await this.getInternalUrlById(siteId, fileId);
|
||||||
return await this.getInternalUrlById(siteId, fileId);
|
|
||||||
}
|
// Add the anchor to the local URL if any.
|
||||||
|
const anchor = CoreUrl.getUrlAnchor(fileUrl);
|
||||||
|
|
||||||
|
return path + (anchor || '');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// The file is not on disk.
|
// The file is not on disk.
|
||||||
// We could not retrieve the file, delete the entries associated with that ID.
|
// We could not retrieve the file, delete the entries associated with that ID.
|
||||||
|
|
|
@ -313,6 +313,11 @@ export class CoreMimetypeUtilsProvider {
|
||||||
if (position > -1) {
|
if (position > -1) {
|
||||||
candidate = candidate.substr(0, position);
|
candidate = candidate.substr(0, position);
|
||||||
}
|
}
|
||||||
|
// Remove anchor if any.
|
||||||
|
position = candidate.indexOf('#');
|
||||||
|
if (position > -1) {
|
||||||
|
candidate = candidate.substr(0, position);
|
||||||
|
}
|
||||||
|
|
||||||
if (EXTENSION_REGEX.test(candidate)) {
|
if (EXTENSION_REGEX.test(candidate)) {
|
||||||
extension = candidate;
|
extension = candidate;
|
||||||
|
|
|
@ -58,6 +58,10 @@ export class CoreUrlUtilsProvider {
|
||||||
* @return URL with params.
|
* @return URL with params.
|
||||||
*/
|
*/
|
||||||
addParamsToUrl(url: string, params?: Record<string, unknown>, anchor?: string, boolToNumber?: boolean): string {
|
addParamsToUrl(url: string, params?: Record<string, unknown>, anchor?: string, boolToNumber?: boolean): string {
|
||||||
|
// Remove any existing anchor to add the params before it.
|
||||||
|
const urlAndAnchor = url.split('#');
|
||||||
|
url = urlAndAnchor[0];
|
||||||
|
|
||||||
let separator = url.indexOf('?') != -1 ? '&' : '?';
|
let separator = url.indexOf('?') != -1 ? '&' : '?';
|
||||||
|
|
||||||
for (const key in params) {
|
for (const key in params) {
|
||||||
|
@ -75,6 +79,15 @@ export class CoreUrlUtilsProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-add the anchor if any.
|
||||||
|
if (urlAndAnchor.length > 1) {
|
||||||
|
// Remove the URL from the array.
|
||||||
|
urlAndAnchor.shift();
|
||||||
|
|
||||||
|
// Use a join in case there is more than one #.
|
||||||
|
url += '#' + urlAndAnchor.join('#');
|
||||||
|
}
|
||||||
|
|
||||||
if (anchor) {
|
if (anchor) {
|
||||||
url += '#' + anchor;
|
url += '#' + anchor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,4 +203,32 @@ export class CoreUrl {
|
||||||
&& CoreText.removeEndingSlash(partsA?.path) === CoreText.removeEndingSlash(partsB?.path);
|
&& CoreText.removeEndingSlash(partsA?.path) === CoreText.removeEndingSlash(partsB?.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the anchor of a URL. If there's more than one they'll all be returned, separated by #.
|
||||||
|
* E.g. myurl.com#foo=1#bar=2 will return #foo=1#bar=2.
|
||||||
|
*
|
||||||
|
* @param url URL.
|
||||||
|
* @return Anchor, undefined if no anchor.
|
||||||
|
*/
|
||||||
|
static getUrlAnchor(url: string): string | undefined {
|
||||||
|
const firstAnchorIndex = url.indexOf('#');
|
||||||
|
if (firstAnchorIndex === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.substr(firstAnchorIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the anchor from a URL.
|
||||||
|
*
|
||||||
|
* @param url URL.
|
||||||
|
* @return URL without anchor if any.
|
||||||
|
*/
|
||||||
|
static removeUrlAnchor(url: string): string {
|
||||||
|
const urlAndAnchor = url.split('#');
|
||||||
|
|
||||||
|
return urlAndAnchor[0];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue