Skip to content

Commit

Permalink
Merge pull request #905 from endlessm/T35137-download-when-offline
Browse files Browse the repository at this point in the history
Support "downloading" a content pack without an internet connection
  • Loading branch information
manuq authored Jan 8, 2024
2 parents 1f0d743 + 0b773ee commit 9179799
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 80 deletions.
23 changes: 2 additions & 21 deletions kolibri_explore_plugin/assets/src/components/DiscoveryNavBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import { mapMutations } from 'vuex';
import { assets } from 'ek-components';
import commonExploreStrings from '../views/commonExploreStrings';
import isOfflineMixin from '../mixins/isOfflineMixin';
import { PageNames } from '../constants';
export default {
Expand All @@ -88,12 +89,7 @@
MessageReplyTextOutlineIcon,
ArrowDownIcon,
},
mixins: [commonExploreStrings],
data() {
return {
isOffline: false,
};
},
mixins: [commonExploreStrings, isOfflineMixin],
computed: {
logo() {
return assets.EndlessLogo;
Expand All @@ -116,15 +112,6 @@
return !!plugin_data.androidApplicationId && !!plugin_data.windowsApplicationId;
},
},
created() {
this.isOffline = !navigator.onLine;
window.addEventListener('offline', this.onOffline);
window.addEventListener('online', this.onOnline);
},
destroyed() {
window.removeEventListener('offline', this.onOffline);
window.removeEventListener('online', this.onOnline);
},
methods: {
...mapMutations({
setSearchResult: 'topicsRoot/SET_SEARCH_RESULT',
Expand Down Expand Up @@ -158,12 +145,6 @@
currentIsSearch() {
return this.$route.name === PageNames.SEARCH;
},
onOffline() {
this.isOffline = true;
},
onOnline() {
this.isOffline = false;
},
onLogoClick(event) {
if (event.ctrlKey) {
this.$store.commit('topicsRoot/SET_SHOW_SIDE_NAV', true);
Expand Down
24 changes: 24 additions & 0 deletions kolibri_explore_plugin/assets/src/mixins/isOfflineMixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export default {
data() {
return {
isOffline: false,
};
},
created() {
this.isOffline = !navigator.onLine;
window.addEventListener('offline', this.onOffline);
window.addEventListener('online', this.onOnline);
},
destroyed() {
window.removeEventListener('offline', this.onOffline);
window.removeEventListener('online', this.onOnline);
},
methods: {
onOffline() {
this.isOffline = true;
},
onOnline() {
this.isOffline = false;
},
},
};
27 changes: 11 additions & 16 deletions kolibri_explore_plugin/assets/src/views/welcome/PackReadyPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<b-button
pill
class="mb-4 mt-4"
:disabled="isOffline"
:disabled="isDownloadRequired && isOffline"
variant="primary"
@click="downloadContent"
>
Expand All @@ -28,40 +28,35 @@
<script>
import { PageNames } from '../../constants';
import isOfflineMixin from '../../mixins/isOfflineMixin';
import { getCollectionInfo } from '../../modules/coreExplore/utils';
import WelcomeBase from './WelcomeBase';
export default {
name: 'PackReadyPage',
components: {
WelcomeBase,
},
mixins: [isOfflineMixin],
data() {
return {
isOffline: false,
PageNames,
isDownloadRequired: true,
};
},
created() {
this.isOffline = !navigator.onLine;
window.addEventListener('offline', this.onOffline);
window.addEventListener('online', this.onOnline);
},
destroyed() {
window.removeEventListener('offline', this.onOffline);
window.removeEventListener('online', this.onOnline);
mounted() {
return getCollectionInfo(this.$route.params.grade, this.$route.params.name).then(
collectionsInfo => {
this.isDownloadRequired = collectionsInfo.isDownloadRequired;
}
);
},
methods: {
downloadContent() {
const grade = this.$route.params.grade;
const name = this.$route.params.name;
this.$router.push({ name: PageNames.DOWNLOAD, params: { grade, name } });
},
onOffline() {
this.isOffline = true;
},
onOnline() {
this.isOffline = false;
},
},
$trs: {
packReadyTitle: {
Expand Down
74 changes: 31 additions & 43 deletions kolibri_explore_plugin/collectionviews.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright 2022-2023 Endless OS Foundation LLC
# SPDX-License-Identifier: GPL-2.0-or-later
import itertools
import logging
import os
import time
Expand Down Expand Up @@ -147,12 +148,20 @@ def set_availability(self, free_space_gb):
def get_channels_count(self):
return len(self.get_channel_ids())

def get_channelimport_tasks(self):
def is_download_required(self):
return any(
itertools.chain(
self.iter_channelimport_tasks(),
self.iter_contentimport_tasks(),
self.iter_contentthumbnail_tasks(),
)
)

def iter_channelimport_tasks(self):
"""Return a serializable object to create channelimport tasks
For all the channels in this content manifest.
"""
tasks = []
for channel_id, channel_version in self.get_latest_channels():
metadata = get_channel_metadata(channel_id)
if metadata and metadata.version >= channel_version:
Expand All @@ -161,16 +170,14 @@ def get_channelimport_tasks(self):
"already present"
)
continue
tasks.append(get_remotechannelimport_task(channel_id))
return tasks
yield get_remotechannelimport_task(channel_id)

def get_extra_channelimport_tasks(self):
def iter_extra_channelimport_tasks(self):
"""Return a serializable object to create extra channelimport tasks
For all channels featured in Endless Key content manifests. In addition
to the channel metadata, all thumbnails are downloaded.
"""
tasks = []
for channel_id, channel_version in self.get_latest_extra_channels():
# Check if the channel metadata and thumbnails are already
# available.
Expand All @@ -192,21 +199,15 @@ def get_extra_channelimport_tasks(self):
)
continue

tasks.append(
get_remoteimport_task(
channel_id, node_ids=[], all_thumbnails=True
)
yield get_remoteimport_task(
channel_id, node_ids=[], all_thumbnails=True
)

return tasks

def get_contentimport_tasks(self):
def iter_contentimport_tasks(self):
"""Return a serializable object to create contentimport tasks
For all the channels in this content manifest.
"""
tasks = []

for channel_id in self.get_channel_ids():
channel_metadata = get_channel_metadata(channel_id)
node_ids = list(
Expand All @@ -226,38 +227,28 @@ def get_contentimport_tasks(self):
)
continue

tasks.append(
get_remotecontentimport_task(
channel_id, channel_metadata.name, node_ids
)
yield get_remotecontentimport_task(
channel_id, channel_metadata.name, node_ids
)

return tasks

def get_applyexternaltags_tasks(self):
def iter_applyexternaltags_tasks(self):
"""Return a serializable object to create applyexternaltags tasks
As defined in this content manifest metadata.
"""
if "tagged_node_ids" not in self.metadata:
return []

tasks = []

for tagged in self.metadata["tagged_node_ids"]:
node_id = tagged["node_id"]
tags = tagged["tags"]
tasks.append(get_applyexternaltags_task(node_id, tags))

return tasks
yield get_applyexternaltags_task(node_id, tags)

def get_contentthumbnail_tasks(self):
def iter_contentthumbnail_tasks(self):
"""Return a serializable object to create thumbnail contentimport tasks
For all the channels in this content manifest.
"""
tasks = []

for channel_id in self.get_channel_ids():
# Check if the desired thumbnail nodes are already available.
num_resources, _, _ = get_import_export_data(
Expand All @@ -273,14 +264,10 @@ def get_contentthumbnail_tasks(self):
)
continue

tasks.append(
get_remotecontentimport_task(
channel_id, node_ids=[], all_thumbnails=True
)
yield get_remotecontentimport_task(
channel_id, node_ids=[], all_thumbnails=True
)

return tasks

def _get_node_ids_for_channel(self, channel_metadata, channel_id):
"""Get node IDs regardless of the version
Expand Down Expand Up @@ -573,29 +560,29 @@ def _set_next_stage(self, user):
while not tasks and self._stage != DownloadStage.COMPLETED:
self._stage = DownloadStage(self._stage + 1)
if self._stage == DownloadStage.IMPORTING_CHANNELS:
tasks = self._content_manifest.get_channelimport_tasks()
tasks = self._content_manifest.iter_channelimport_tasks()
elif self._stage == DownloadStage.IMPORTING_CONTENT:
tasks = self._content_manifest.get_contentimport_tasks()
tasks = self._content_manifest.iter_contentimport_tasks()
elif self._stage == DownloadStage.APPLYING_EXTERNAL_TAGS:
tasks = self._content_manifest.get_applyexternaltags_tasks()
tasks = self._content_manifest.iter_applyexternaltags_tasks()

if self._stage == DownloadStage.COMPLETED:
logger.info("Download completed!")

# Download the manifest content thumbnails and the extra channels
# in the background.
thumbnail_tasks = (
self._content_manifest.get_contentthumbnail_tasks()
self._content_manifest.iter_contentthumbnail_tasks()
)
extra_channel_tasks = (
self._content_manifest.get_extra_channelimport_tasks()
self._content_manifest.iter_extra_channelimport_tasks()
)
for task in thumbnail_tasks + extra_channel_tasks:
for task in itertools.chain(thumbnail_tasks, extra_channel_tasks):
BackgroundTask.create_from_task_data(task)
logger.info("Starting background download tasks")
enqueue_next_background_task()

self._tasks_pending = tasks
self._tasks_pending = list(tasks)
self._tasks_previously_completed.extend(self._tasks_completed)
self._tasks_completed = []
logger.info(f"Started download stage: {self._stage.name}")
Expand Down Expand Up @@ -685,6 +672,7 @@ def _get_collections_info_by_grade_name(grade, name):
"metadata": manifest.metadata,
"available": manifest.available,
"channelsCount": manifest.get_channels_count(),
"isDownloadRequired": manifest.is_download_required(),
}


Expand Down
2 changes: 2 additions & 0 deletions kolibri_explore_plugin/test/test_collectionviews.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def test_get_collection_info():
"metadata": collection_data["metadata"],
"available": True,
"channelsCount": len(collection_data["channels"]),
"isDownloadRequired": True,
}
}

Expand Down Expand Up @@ -81,6 +82,7 @@ def test_get_all_collections_info():
"metadata": collection_data["metadata"],
"available": True,
"channelsCount": len(collection_data["channels"]),
"isDownloadRequired": True,
}
)
expected_data = {"allCollectionsInfo": all_collections_info}
Expand Down

0 comments on commit 9179799

Please sign in to comment.