MOBILE-3437 files: Ignore downloaded files on prefetch calculations

main
Pau Ferrer Ocaña 2020-06-03 10:09:40 +02:00
parent 4f9adba63e
commit 2052080673
6 changed files with 109 additions and 78 deletions

View File

@ -28,6 +28,7 @@ import { CoreGroupsProvider } from '@providers/groups';
import { AddonModFeedbackSyncProvider } from './sync'; import { AddonModFeedbackSyncProvider } from './sync';
import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; import { CoreFilterHelperProvider } from '@core/filter/providers/helper';
import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; import { CorePluginFileDelegate } from '@providers/plugin-file-delegate';
import { CoreWSExternalFile } from '@providers/ws';
/** /**
* Handler to prefetch feedbacks. * Handler to prefetch feedbacks.
@ -68,26 +69,31 @@ export class AddonModFeedbackPrefetchHandler extends CoreCourseActivityPrefetchH
* @param single True if we're downloading a single module, false if we're downloading a whole section. * @param single True if we're downloading a single module, false if we're downloading a whole section.
* @return Promise resolved with the list of files. * @return Promise resolved with the list of files.
*/ */
getFiles(module: any, courseId: number, single?: boolean): Promise<any[]> { async getFiles(module: any, courseId: number, single?: boolean): Promise<CoreWSExternalFile[]> {
let files = []; let files = [];
return this.feedbackProvider.getFeedback(courseId, module.id).then((feedback) => { const feedback = await this.feedbackProvider.getFeedback(courseId, module.id);
// Get intro files and page after submit files. // Get intro files and page after submit files.
files = feedback.pageaftersubmitfiles || []; files = feedback.pageaftersubmitfiles || [];
files = files.concat(this.getIntroFilesFromInstance(module, feedback)); files = files.concat(this.getIntroFilesFromInstance(module, feedback));
try {
const response = await this.feedbackProvider.getItems(feedback.id);
return this.feedbackProvider.getItems(feedback.id);
}).then((response) => {
response.items.forEach((item) => { response.items.forEach((item) => {
files = files.concat(item.itemfiles); files = files.concat(item.itemfiles.map((file) => {
file.fileurl = file.fileurl || file.url;
return file;
}));
}); });
return files; } catch (e) {
}).catch(() => { // Ignore errors.
// Any error, return the list we have. }
return files;
}); return files;
} }
/** /**
@ -97,7 +103,7 @@ export class AddonModFeedbackPrefetchHandler extends CoreCourseActivityPrefetchH
* @param courseId Course ID. * @param courseId Course ID.
* @return Promise resolved with list of intro files. * @return Promise resolved with list of intro files.
*/ */
getIntroFiles(module: any, courseId: number): Promise<any[]> { getIntroFiles(module: any, courseId: number): Promise<CoreWSExternalFile[]> {
return this.feedbackProvider.getFeedback(courseId, module.id).catch(() => { return this.feedbackProvider.getFeedback(courseId, module.id).catch(() => {
// Not found, return undefined so module description is used. // Not found, return undefined so module description is used.
}).then((feedback) => { }).then((feedback) => {

View File

@ -111,7 +111,7 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan
let files = lesson.mediafiles || []; let files = lesson.mediafiles || [];
files = files.concat(this.getIntroFilesFromInstance(module, lesson)); files = files.concat(this.getIntroFilesFromInstance(module, lesson));
return this.pluginFileDelegate.getFilesSize(files); return this.pluginFileDelegate.getFilesDownloadSize(files);
}).then((res) => { }).then((res) => {
result = res; result = res;

View File

@ -97,7 +97,7 @@ export class AddonModWikiPrefetchHandler extends CoreCourseActivityPrefetchHandl
siteId = this.sitesProvider.getCurrentSiteId(); siteId = this.sitesProvider.getCurrentSiteId();
promises.push(this.getFiles(module, courseId, single, siteId).then((files) => { promises.push(this.getFiles(module, courseId, single, siteId).then((files) => {
return this.pluginFileDelegate.getFilesSize(files); return this.pluginFileDelegate.getFilesDownloadSize(files);
})); }));
promises.push(this.getAllPages(module, courseId, false, true, siteId).then((pages) => { promises.push(this.getAllPages(module, courseId, false, true, siteId).then((pages) => {

View File

@ -19,6 +19,7 @@ import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreCourseProvider } from '../providers/course'; import { CoreCourseProvider } from '../providers/course';
import { CoreWSExternalFile } from '@providers/ws';
import { CoreCourseModulePrefetchHandler } from '../providers/module-prefetch-delegate'; import { CoreCourseModulePrefetchHandler } from '../providers/module-prefetch-delegate';
import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; import { CoreFilterHelperProvider } from '@core/filter/providers/helper';
import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; import { CorePluginFileDelegate } from '@providers/plugin-file-delegate';
@ -114,7 +115,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param module The module object returned by WS. * @param module The module object returned by WS.
* @return List of files. * @return List of files.
*/ */
getContentDownloadableFiles(module: any): any[] { getContentDownloadableFiles(module: any): CoreWSExternalFile[] {
const files = []; const files = [];
if (module.contents && module.contents.length) { if (module.contents && module.contents.length) {
@ -139,7 +140,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
*/ */
getDownloadSize(module: any, courseId: number, single?: boolean): Promise<{ size: number, total: boolean }> { getDownloadSize(module: any, courseId: number, single?: boolean): Promise<{ size: number, total: boolean }> {
return this.getFiles(module, courseId).then((files) => { return this.getFiles(module, courseId).then((files) => {
return this.pluginFileDelegate.getFilesSize(files); return this.pluginFileDelegate.getFilesDownloadSize(files);
}).catch(() => { }).catch(() => {
return { size: -1, total: false }; return { size: -1, total: false };
}); });
@ -166,7 +167,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param single True if we're downloading a single module, false if we're downloading a whole section. * @param single True if we're downloading a single module, false if we're downloading a whole section.
* @return Promise resolved with the list of files. * @return Promise resolved with the list of files.
*/ */
getFiles(module: any, courseId: number, single?: boolean): Promise<any[]> { getFiles(module: any, courseId: number, single?: boolean): Promise<CoreWSExternalFile[]> {
// To be overridden. // To be overridden.
return Promise.resolve([]); return Promise.resolve([]);
} }
@ -179,7 +180,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
* @return Promise resolved with list of intro files. * @return Promise resolved with list of intro files.
*/ */
getIntroFiles(module: any, courseId: number, ignoreCache?: boolean): Promise<any[]> { getIntroFiles(module: any, courseId: number, ignoreCache?: boolean): Promise<CoreWSExternalFile[]> {
return Promise.resolve(this.getIntroFilesFromInstance(module)); return Promise.resolve(this.getIntroFilesFromInstance(module));
} }
@ -190,7 +191,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param instance The instance to get the intro files (book, assign, ...). If not defined, module will be used. * @param instance The instance to get the intro files (book, assign, ...). If not defined, module will be used.
* @return List of intro files. * @return List of intro files.
*/ */
getIntroFilesFromInstance(module: any, instance?: any): any[] { getIntroFilesFromInstance(module: any, instance?: any): CoreWSExternalFile[] {
if (instance) { if (instance) {
if (typeof instance.introfiles != 'undefined') { if (typeof instance.introfiles != 'undefined') {
return instance.introfiles; return instance.introfiles;

View File

@ -1397,7 +1397,7 @@ export class CoreFilepoolProvider {
* @param html HTML code. * @param html HTML code.
* @return List of fake file objects with file URLs. * @return List of fake file objects with file URLs.
*/ */
extractDownloadableFilesFromHtmlAsFakeFileObjects(html: string): any[] { extractDownloadableFilesFromHtmlAsFakeFileObjects(html: string): CoreWSExternalFile[] {
const urls = this.extractDownloadableFilesFromHtml(html); const urls = this.extractDownloadableFilesFromHtml(html);
// Convert them to fake file objects. // Convert them to fake file objects.
@ -1768,46 +1768,50 @@ export class CoreFilepoolProvider {
* @param revision File revision. If not defined, it will be calculated using the URL. * @param revision File revision. If not defined, it will be calculated using the URL.
* @return Promise resolved with the file state. * @return Promise resolved with the file state.
*/ */
getFileStateByUrl(siteId: string, fileUrl: string, timemodified: number = 0, filePath?: string, revision?: number) async getFileStateByUrl(siteId: string, fileUrl: string, timemodified: number = 0, filePath?: string, revision?: number)
: Promise<string> { : Promise<string> {
let fileId; let file;
return this.fixPluginfileURL(siteId, fileUrl, timemodified).then((file) => { try {
file = await this.fixPluginfileURL(siteId, fileUrl, timemodified);
fileUrl = file.fileurl; } catch (e) {
timemodified = file.timemodified || timemodified;
revision = revision || this.getRevisionFromUrl(fileUrl);
fileId = this.getFileIdByUrl(fileUrl);
// Check if the file is in queue (waiting to be downloaded).
return this.hasFileInQueue(siteId, fileId).then(() => {
return CoreConstants.DOWNLOADING;
}).catch(() => {
// Check if the file is being downloaded right now.
const extension = this.mimeUtils.guessExtensionFromUrl(fileUrl),
path = filePath ? filePath : this.getFilePath(siteId, fileId, extension);
return Promise.resolve(path).then((filePath) => {
const downloadId = this.getFileDownloadId(fileUrl, filePath);
if (this.filePromises[siteId] && this.filePromises[siteId][downloadId]) {
return CoreConstants.DOWNLOADING;
}
// File is not being downloaded. Check if it's downloaded and if it's outdated.
return this.hasFileInPool(siteId, fileId).then((entry) => {
if (this.isFileOutdated(entry, revision, timemodified)) {
return CoreConstants.OUTDATED;
} else {
return CoreConstants.DOWNLOADED;
}
}).catch(() => {
return CoreConstants.NOT_DOWNLOADED;
});
});
});
}, () => {
return CoreConstants.NOT_DOWNLOADABLE; return CoreConstants.NOT_DOWNLOADABLE;
}); }
fileUrl = file.fileurl;
timemodified = file.timemodified || timemodified;
revision = revision || this.getRevisionFromUrl(fileUrl);
const fileId = this.getFileIdByUrl(fileUrl);
try {
// Check if the file is in queue (waiting to be downloaded).
await this.hasFileInQueue(siteId, fileId);
return CoreConstants.DOWNLOADING;
} catch (e) {
// Check if the file is being downloaded right now.
const extension = this.mimeUtils.guessExtensionFromUrl(fileUrl);
filePath = filePath || (await this.getFilePath(siteId, fileId, extension));
const downloadId = this.getFileDownloadId(fileUrl, filePath);
if (this.filePromises[siteId] && this.filePromises[siteId][downloadId]) {
return CoreConstants.DOWNLOADING;
}
try {
// File is not being downloaded. Check if it's downloaded and if it's outdated.
const entry = await this.hasFileInPool(siteId, fileId);
if (this.isFileOutdated(entry, revision, timemodified)) {
return CoreConstants.OUTDATED;
}
return CoreConstants.DOWNLOADED;
} catch (e) {
return CoreConstants.NOT_DOWNLOADED;
}
}
} }
/** /**

View File

@ -18,6 +18,8 @@ import { CoreLoggerProvider } from './logger';
import { CoreSitesProvider } from './sites'; import { CoreSitesProvider } from './sites';
import { CoreWSExternalFile } from '@providers/ws'; import { CoreWSExternalFile } from '@providers/ws';
import { FileEntry } from '@ionic-native/file'; import { FileEntry } from '@ionic-native/file';
import { CoreFilepool } from './filepool';
import { CoreConstants } from '@core/constants';
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
import { makeSingleton } from '@singletons/core.singletons'; import { makeSingleton } from '@singletons/core.singletons';
@ -234,6 +236,27 @@ export class CorePluginFileDelegate extends CoreDelegate {
return files; return files;
} }
/**
* Sum the filesizes from a list if they are not downloaded.
*
* @param files List of files to sum its filesize.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with file size and a boolean to indicate if it is the total size or only partial.
*/
async getFilesDownloadSize(files: CoreWSExternalFile[], siteId?: string): Promise<{ size: number, total: boolean }> {
const filteredFiles = [];
await Promise.all(files.map(async (file) => {
const state = await CoreFilepool.instance.getFileStateByUrl(siteId, file.fileurl, file.timemodified);
if (state != CoreConstants.DOWNLOADED && state != CoreConstants.NOT_DOWNLOADABLE) {
filteredFiles.push(file);
}
}));
return this.getFilesSize(filteredFiles, siteId);
}
/** /**
* Sum the filesizes from a list of files checking if the size will be partial or totally calculated. * Sum the filesizes from a list of files checking if the size will be partial or totally calculated.
* *
@ -241,27 +264,24 @@ export class CorePluginFileDelegate extends CoreDelegate {
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @return Promise resolved with file size and a boolean to indicate if it is the total size or only partial. * @return Promise resolved with file size and a boolean to indicate if it is the total size or only partial.
*/ */
getFilesSize(files: CoreWSExternalFile[], siteId?: string): Promise<{ size: number, total: boolean }> { async getFilesSize(files: CoreWSExternalFile[], siteId?: string): Promise<{ size: number, total: boolean }> {
const promises = [], const result = {
result = { size: 0,
size: 0, total: true
total: true };
};
files.forEach((file) => { await Promise.all(files.map(async (file) => {
promises.push(this.getFileSize(file, siteId).then((size) => { const size = await this.getFileSize(file, siteId);
if (typeof size == 'undefined') {
// We don't have the file size, cannot calculate its total size.
result.total = false;
} else {
result.size += size;
}
}));
});
return Promise.all(promises).then(() => { if (typeof size == 'undefined') {
return result; // We don't have the file size, cannot calculate its total size.
}); result.total = false;
} else {
result.size += size;
}
}));
return result;
} }
/** /**