def audio_invalid_files(audio_data, document_file): args_data = get_content_node_args(audio_data) contentnode_kwargs = get_content_node_kwargs(audio_data) contentnode_kwargs['files'] = [] # clear files because added one above audio = AudioNode(*args_data, **contentnode_kwargs) audio.add_file(document_file) return audio
def audio(audio_file, audio_data, channel): args_data = get_content_node_args(audio_data) contentnode_kwargs = get_content_node_kwargs(audio_data) audio = AudioNode(*args_data, **contentnode_kwargs) audio.add_file(audio_file) channel.add_child(audio) audio_data['files'].append(audio_file) # save it so we can compare later return audio
def test_invalid_mp3_fails(self, invalid_audio_file): node = AudioNode('audio-src-id', "Document", licenses.PUBLIC_DOMAIN, thumbnail=None) node.add_file(invalid_audio_file) config.THUMBNAILS = True filenames = node.process_files()
def test_generate_thumbnail_from_audio(self, audio_file): node = AudioNode('audio-src-id', "Audio", licenses.PUBLIC_DOMAIN, thumbnail=None) node.add_file(audio_file) config.THUMBNAILS = True filenames = node.process_files() assert len(filenames) == 2, 'expected two filenames' self.check_has_thumbnail(node)
def test_non_existent_mp3_fails(self): node = AudioNode('audio-src-id', "Document", licenses.PUBLIC_DOMAIN, thumbnail=None) non_existent_path = 'does/not/exist.mp3' document_file = AudioFile(non_existent_path, language='en') node.add_file(document_file) config.THUMBNAILS = True filenames = node.process_files() assert filenames == [None], 'expected one None (the non existent mp3)' assert len(config.FAILED_FILES) == 1, 'expected one failed file'
def create_content_nodes(self, channel): """ This function uses the methods `add_child` and `add_file` to build the hierarchy of topic nodes (nested folder structure) and content nodes. Every content node is associated with one or more files. """ content_nodes_folder = TopicNode( source_id='uniqid001', title='Content Nodes', description='Put folder description here', author=None, language=getlang('en').id, thumbnail=None, ) channel.add_child(content_nodes_folder) # AUDIO audio_nodes_folder = TopicNode( source_id='uniqid002', title='Audio Files Folder', description='Put folder description here', author=None, language=getlang('en').id, thumbnail=None, ) content_nodes_folder.add_child(audio_nodes_folder) audio_node = AudioNode( source_id='uniqid003', title='Whale sounds', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail=None, files=[], ) audio_nodes_folder.add_child(audio_node) audio_file = AudioFile( path= './content/ricecooker-channel-files/Whale_sounds.mp3', # note path can also be a URL language=getlang('en').id) audio_node.add_file(audio_file) # DOCUMENTS documents_folder = TopicNode( source_id='uniqid004', title='Document Nodes', description='Put folder description here', author=None, language=getlang('en').id, thumbnail=None, ) content_nodes_folder.add_child(documents_folder) document_node = DocumentNode( source_id='uniqid005', title= 'The Supreme Court\u2019s Ruling in Brown vs. Board of Education', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail=None, files=[ DocumentFile( path= './content/ricecooker-channel-files/brown-vs-board-of-education.pdf', language=getlang('en').id) ]) documents_folder.add_child(document_node) # HTML5 APPS html5apps_folder = TopicNode( source_id='uniqid006', title='HTML5App Nodes', description='Put folder description here', author=None, language=getlang('en').id, thumbnail=None, ) content_nodes_folder.add_child(html5apps_folder) html5_node = HTML5AppNode( source_id='uniqid007', title='HTMLWeb capabilities test', author='First Last (author\'s name)', description= 'Tests different HTML/JS capabilities. What capabilities are allowed and disallowed by the sandboxed iframe used to render HTML5App nodes on Kolibri.', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail='./content/ricecooker-channel-files/html5_tests.jpg', files=[ HTMLZipFile( path='./content/ricecooker-channel-files/html5_tests.zip', language=getlang('en').id) ]) html5apps_folder.add_child(html5_node) html5_node2 = HTML5AppNode( source_id='uniqid008', title='Sample Vue.js app', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail='./content/ricecooker-channel-files/html5_vuejs.jpg', files=[ HTMLZipFile( path='./content/ricecooker-channel-files/html5_vuejs.zip', language=getlang('en').id) ]) html5apps_folder.add_child(html5_node2) # VIDEOS videos_folder = TopicNode( source_id='uniqid009', title='Video Nodes', description='Put folder description here', author=None, language=getlang('en').id, thumbnail=None, ) content_nodes_folder.add_child(videos_folder) video_node = VideoNode( source_id='uniqid010', title='Wave particle duality explained in 2 mins', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), derive_thumbnail=True, # video-specicig flag thumbnail=None, files=[ VideoFile( path= './content/ricecooker-channel-files/Wave_particle_duality.mp4', language=getlang('en').id) ]) videos_folder.add_child(video_node) youtube_id = 'VJyk81HmcZQ' video_node2 = VideoNode( source_id=youtube_id, # usually set source_id to youtube_id title='Estimating division that results in non whole numbers', author='Sal Khan', description='Video description would go here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Khan Academy'), derive_thumbnail=True, # video-specicig flag thumbnail=None, files=[ YouTubeVideoFile(youtube_id=youtube_id, high_resolution=False, language='en'), YouTubeSubtitleFile(youtube_id=youtube_id, language='ko') ]) videos_folder.add_child(video_node2)
def build_tree(parent_node,root_folder): #check ini file if not os.path.exists(os.path.join(root_folder,'metadata.ini')): raise FileNotFoundException("Missing 'metadata.ini' configuration file in {0}".format([root_folder])) content_config = ini_to_json(os.path.join(root_folder,'metadata.ini')) #loop directory for file in os.listdir(root_folder): # separate file name from ext file_name, ext = parse_file_name(file) #check to see is file exists in metadata.ini file if not content_config.get( file_name ): if ext != 'ini': warnings.warn("File {} has no configuration in {}... SKIPPED".format(file,os.path.join(root_folder,'metadata.ini'))) #if file is a directory, create TOPIC node and step into dir elif os.path.isdir(os.path.join(root_folder,file)) and file != '.' and file != '..': topic = TopicNode( source_id = content_config[file_name]["__name__"], title = content_config[file_name]["title"], author = content_config[file_name].get("author"), description = content_config[file_name].get("description"), thumbnail = content_config[file_name].get("thumbnail"), ) parent_node.add_child(topic) build_tree(topic,os.path.join(root_folder,file)) elif ext == "mp4": #in VIDEO: child = VideoNode( source_id = content_config[file_name]["__name__"], title = content_config[file_name]["title"], license = content_config[file_name].get("license"), author = content_config[file_name].get("author"), description = content_config[file_name].get("description"), derive_thumbnail= True, # video-specific data thumbnail = content_config[file_name].get("thumbnail"), ) add_files(child, [os.path.join(root_folder,file)] + (content_config[file_name].get("files") or [])) parent_node.add_child(child) elif ext == "mp3": #in AUDIO: child = AudioNode( source_id = content_config[file_name]["__name__"], title = content_config[file_name]["title"], license = content_config[file_name].get("license"), author = content_config[file_name].get("author"), description = content_config[file_name].get("description"), thumbnail = content_config[file_name].get("thumbnail"), ) add_files(child, [os.path.join(root_folder,file)] + (content_config[file_name].get("files") or [])) parent_node.add_child(child) elif ext == "pdf": #in DOCUMENT: child = DocumentNode( source_id = content_config[file_name]["__name__"], title = content_config[file_name]["title"], license = content_config[file_name].get("license"), author = content_config[file_name].get("author"), description = content_config[file_name].get("description"), thumbnail = content_config[file_name].get("thumbnail"), ) add_files(child, [os.path.join(root_folder,file)] + (content_config[file_name].get("files") or [])) parent_node.add_child(child) else: continue
def construct_channel(self, *args, **kwargs): """ This method is reponsible for creating a `ChannelNode` object and populating it with `TopicNode` and `ContentNode` children. """ # Create channel ######################################################################## channel = self.get_channel(*args, **kwargs) # uses self.channel_info # Create topics to add to your channel ######################################################################## # Here we are creating a topic named 'Example Topic' exampletopic = TopicNode(source_id="topic-1", title="Example Topic") # TODO: Create your topic here # Now we are adding 'Example Topic' to our channel channel.add_child(exampletopic) # TODO: Add your topic to channel here # You can also add subtopics to topics # Here we are creating a subtopic named 'Example Subtopic' examplesubtopic = TopicNode(source_id="topic-1a", title="Example Subtopic") # TODO: Create your subtopic here # Now we are adding 'Example Subtopic' to our 'Example Topic' exampletopic.add_child(examplesubtopic) # TODO: Add your subtopic to your topic here # Content # You can add documents (pdfs and ePubs), videos, audios, and other content ######################################################################## # let's create a document file called 'Example PDF' document_file = DocumentFile( path="http://www.pdf995.com/samples/pdf.pdf") examplepdf = DocumentNode(title="Example PDF", source_id="example-pdf", files=[document_file], license=get_license(licenses.PUBLIC_DOMAIN)) # TODO: Create your pdf file here (use any url to a .pdf file) # We are also going to add a video file called 'Example Video' video_file = VideoFile( path= "https://ia600209.us.archive.org/27/items/RiceChef/Rice Chef.mp4") fancy_license = get_license( licenses.SPECIAL_PERMISSIONS, description='Special license for ricecooker fans only.', copyright_holder='The chef video makers') examplevideo = VideoNode(title="Example Video", source_id="example-video", files=[video_file], license=fancy_license) # TODO: Create your video file here (use any url to a .mp4 file) # Finally, we are creating an audio file called 'Example Audio' audio_file = AudioFile( path= "https://ia802508.us.archive.org/5/items/testmp3testfile/mpthreetest.mp3" ) exampleaudio = AudioNode(title="Example Audio", source_id="example-audio", files=[audio_file], license=get_license(licenses.PUBLIC_DOMAIN)) # TODO: Create your audio file here (use any url to a .mp3 file) # Now that we have our files, let's add them to our channel channel.add_child(examplepdf) # Adding 'Example PDF' to your channel exampletopic.add_child( examplevideo) # Adding 'Example Video' to 'Example Topic' examplesubtopic.add_child( exampleaudio) # Adding 'Example Audio' to 'Example Subtopic' # TODO: Add your pdf file to your channel # TODO: Add your video file to your topic # TODO: Add your audio file to your subtopic # the `construct_channel` method returns a ChannelNode that will be # processed by the ricecooker framework return channel
def create_content_nodes(self, channel): """ This function uses the methods `add_child` and `add_file` to build the hierarchy of topic nodes and content nodes. Every content node is associated with the underlying file node. """ content_nodes_folder = TopicNode( source_id='121232ms', title='Content Nodes', description='Put folder description here', author=None, language=getlang('en').id, thumbnail=None, ) channel.add_child(content_nodes_folder) # AUDIO audio_nodes_folder = TopicNode( source_id='138iuh23iu', title='Audio Files', description='Put folder description here', author=None, language=getlang('en').id, thumbnail=None, ) content_nodes_folder.add_child(audio_nodes_folder) audio_node = AudioNode( source_id='940ac8ff', title='Whale sounds', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail=None, files=[], ) audio_nodes_folder.add_child(audio_node) audio_file = AudioFile( path='./content/ricecooker-channel-files/Whale_sounds.mp3', language=getlang('en').id) audio_node.add_file(audio_file) # DOCUMENTS documents_folder = TopicNode( source_id='asanlksnaklsn', title='Document Nodes', description='Put folder description here', author=None, language=getlang('en').id, thumbnail=None, ) content_nodes_folder.add_child(documents_folder) document_node = DocumentNode( source_id='80b7136f', title= 'The Supreme Court\u2019s Ruling in Brown vs. Board of Education', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail=None, files=[ DocumentFile( path= './content/ricecooker-channel-files/commonlit_the-supreme-court-s-ruling-in-brown-vs-board-of-education_student.pdf', language=getlang('en').id) ]) documents_folder.add_child(document_node) # HTML5 APPS html5apps_folder = TopicNode( source_id='asasa331', title='HTML5App Nodes', description='Put folder description here', author=None, language=getlang('en').id, thumbnail=None, ) content_nodes_folder.add_child(html5apps_folder) html5_node_a = HTML5AppNode( source_id='302723b4', title='Sample React app', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail='./content/ricecooker-channel-files/html5_react.jpg', files=[ HTMLZipFile( path='./content/ricecooker-channel-files/html5_react.zip', language=getlang('en').id) ]) html5apps_folder.add_child(html5_node_a) html5_node_b = HTML5AppNode( source_id='3f91184e', title='Sample Vue.js app', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail='./content/ricecooker-channel-files/html5_vuejs.jpg', files=[ HTMLZipFile( path='./content/ricecooker-channel-files/html5_vuejs.zip', language=getlang('en').id) ]) html5apps_folder.add_child(html5_node_b) html5_node_c = HTML5AppNode( source_id='0aec4296', title='Sample wget-scraped web content', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail= './content/ricecooker-channel-files/html5_wget_scraped.jpg', files=[ HTMLZipFile( path= './content/ricecooker-channel-files/html5_wget_scraped.zip', language=getlang('en').id) ]) html5apps_folder.add_child(html5_node_c) # VIDEOS videos_folder = TopicNode( source_id='121213m3m3', title='Video Nodes', description='Put folder description here', author=None, language=getlang('en').id, thumbnail=None, ) content_nodes_folder.add_child(videos_folder) video_node = VideoNode( source_id='9e355995', title='Wave particle duality explained in 2 mins', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), derive_thumbnail=True, # video-specicig flag thumbnail=None, files=[ VideoFile( path= './content/ricecooker-channel-files/Wave_particle_duality.mp4', language=getlang('en').id) ]) videos_folder.add_child(video_node)
def construct_channel(*args, **kwargs): """ Start by creating your channel """ # Let's start by creating your own channel (replace <placeholders> with your own values) channel = ChannelNode( source_domain="vuzy.com", # (e.g. "jamiealexandre.com") source_id="apprentice", # (e.g. "my-sushi-chef") title="My First Try", # (e.g. "My Sushi Chef Channel") ) """ Create topics to add to your channel """ # Here we are creating a topic named 'Example Topic' exampletopic = TopicNode(source_id="topic-1", title="Example Topic") # TODO: Create your topic here # Now we are adding 'Example Topic' to our channel channel.add_child(exampletopic) # TODO: Add your topic to channel here """ You can also add subtopics to topics """ # Here we are creating a subtopic named 'Example Subtopic' examplesubtopic = TopicNode(source_id="topic-1a", title="Example Subtopic") # TODO: Create your subtopic here # Now we are adding 'Example Subtopic' to our 'Example Topic' exampletopic.add_child(examplesubtopic) # TODO: Add your subtopic to your topic here """ You can also add pdfs, videos, and audio files to your channel """ # Next, let's create a document file called 'Example PDF' examplepdf = DocumentNode( title="Example PDF", source_id="example-pdf", files=[DocumentFile(path="http://www.pdf995.com/samples/pdf.pdf")], license=licenses.CC_BY_SA) # TODO: Create your pdf file here (use any url to a .pdf file) # We are also going to add a video file called 'Example Video' examplevideo = VideoNode( title="Example Video", source_id="example-video", files=[ VideoFile( path= "https://ia600209.us.archive.org/27/items/RiceChef/Rice Chef.mp4" ) ], license=licenses.CC_BY_SA) # TODO: Create your video file here (use any url to a .mp4 file) # Finally, we are creating an audio file called 'Example Audio' exampleaudio = AudioNode( title="Example Audio", source_id="example-audio", files=[ AudioFile( path= "https://ia802508.us.archive.org/5/items/testmp3testfile/mpthreetest.mp3" ) ], license=licenses.CC_BY_SA) # TODO: Create your audio file here (use any url to a .mp3 file) # Now that we have our files, let's add them to our channel channel.add_child(examplepdf) # Adding 'Example PDF' to your channel exampletopic.add_child( examplevideo) # Adding 'Example Video' to 'Example Topic' examplesubtopic.add_child( exampleaudio) # Adding 'Example Audio' to 'Example Subtopic' # TODO: Add your pdf file to your channel # TODO: Add your video file to your topic # TODO: Add your audio file to your subtopic return channel
def make_random_subtree(parent, depth): for i in range(45): istr = str(i) title = ''.join( random.choice(string.ascii_uppercase + string.digits) for _ in range(10)) description = ''.join( random.choice(string.ascii_uppercase + string.digits) for _ in range(100)) typ = random.choice("tttttttvadh") if typ == 't': topic = TopicNode( source_id=title + istr, title=title, description=description, author=None, language=getlang('en').id, thumbnail=None, ) parent.add_child(topic) if depth > 0: make_random_subtree(topic, depth - 1) elif typ == 'a': content11a = AudioNode( source_id='940ac8ff' + istr, title='Whale sounds', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail=None, files=[], ) parent.add_child(content11a) audio_file = AudioFile( path='./content/ricecooker-channel-files/Whale_sounds.mp3', language=getlang('en').id) content11a.add_file(audio_file) elif typ == 'd': content12a = DocumentNode( source_id='80b7136f' + istr, title= 'The Supreme Court\u2019s Ruling in Brown vs. Board of Education', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail=None, files=[ DocumentFile( path= './content/ricecooker-channel-files/commonlit_the-supreme-court-s-ruling-in-brown-vs-board-of-education_student.pdf', language=getlang('en').id) ]) parent.add_child(content12a) elif typ == 'h': content13a = HTML5AppNode( source_id='302723b4' + istr, title='Sample React app', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), thumbnail='./content/ricecooker-channel-files/html5_react.jpg', files=[ HTMLZipFile( path= './content/ricecooker-channel-files/html5_react.zip', language=getlang('en').id) ]) parent.add_child(content13a) elif type == 'v': content14a = VideoNode( source_id='9e355995', title='Wave particle duality explained in 2 mins', author='First Last (author\'s name)', description='Put file description here', language=getlang('en').id, license=get_license(licenses.CC_BY, copyright_holder='Copyright holder name'), derive_thumbnail=True, # video-specicig flag thumbnail=None, files=[ VideoFile( path= './content/ricecooker-channel-files/Wave_particle_duality.mp4', language=getlang('en').id) ]) parent.add_child(content14a)