def publish(self, channeltitle, url, title=""): """Publish the video (channeltitle, url, title) """ adminuser = get_user('coco') try: channel = Channel.objects.get(Q(slug=channeltitle) | Q(title=channeltitle)) except: channel = Channel(creator=adminuser, contributor=adminuser, title=channeltitle, slug=slugify(channeltitle)) channel.save() if not title: title = os.path.basename(url) slug = slugify(title) try: chapter = Chapter.objects.get(title=title, channel=channel) except Chapter.DoesNotExist: chapter = Chapter(creator=adminuser, contributor=adminuser, channel=channel, title=title, slug=slug) chapter.save() activity = Activity(creator=adminuser, contributor=adminuser, title=title, slug=slug, chapter=chapter) activity.save() # Get length duration = 0 dur = subprocess.check_output('gst-discoverer-1.0 "%s" | grep Duration' % url, shell=True) if dur: info = re.search('(\d+):(\d+):(\d+.\d+)', dur) if info: duration = float(info.group(1)) * 24 * 60 + float(info.group(2)) * 60 + float(info.group(3)) vid = Video(creator=adminuser, contributor=adminuser, activity=activity, title=title, url=url, slug=slug, duration=duration) # Save anyway, so that we have a valid video even if thumbnailing fails vid.save() # Get thumbnail thumbnail_name = os.tmpnam() + ".jpg" # Low-dependency thumbnailer subprocess.call('gst-launch-1.0 gnlurisource "uri=%s" start=4000000 duration=2000000 ! videoconvert ! jpegenc ! filesink location=%s' % (url, thumbnail_name), shell=True) if os.path.exists(thumbnail_name): with open(thumbnail_name, 'rb') as f: vid.thumbnail.save(os.path.basename(thumbnail_name), File(f)) for o in (vid.activity, vid.activity.chapter, vid.activity.chapter.channel): if not o.thumbnail: # Use same thumbnail with open(thumbnail_name, 'rb') as f: o.thumbnail.save(os.path.basename(thumbnail_name), File(f)) o.save() os.unlink(thumbnail_name) vid.save()
def _info(self, channel, info): """Import video/channel info from info.json files. Params: channel_title info.json """ with open(info, 'r') as f: data = json.load(f) self.stdout.write("Saving %s\n" % data.get("title")) adminuser = get_user('coco') dirname = os.path.dirname(os.path.abspath(info)) try: c = Channel.objects.get(Q(title=channel) | Q(slug=channel)) except Channel.DoesNotExist: c = Channel(creator=adminuser, contributor=adminuser, title=channel, slug=slugify(channel)) c.save() title = data.get("title", "Titre inconnu") if title.startswith("Langage C -"): title = title.replace("Langage C -", "") try: chapter = Chapter.objects.get(title=title, channel=c) except Chapter.DoesNotExist: chapter = Chapter(creator=adminuser, contributor=adminuser, channel=c, title=title, slug=slugify(title)) chapter.save() descr = "%s - %s" % (data.get("date", "Date inconnue"), data.get("author", "Auteur inconnu")) activity = Activity(creator=adminuser, contributor=adminuser, title=title, chapter=chapter, description=descr) activity.save() url = data.get("url", "") if not url: # Default url url = "https://comin-ocw.org/contents/%s/camera.mp4" % dirname.split('/contents/')[-1] vid = Video(creator=adminuser, contributor=adminuser, activity=activity, title=title, url=url) # Note: length is not initialized. We will get its duration # from the package just below. # Migrate license info licenses = [license for license in ('cc-by', 'cc-by-nc', 'cc-by-sa', 'cc-by-nc-sa') if data.get(license)] if licenses: # Associate license vid.license = License.objects.get(slug=licenses[0]) pic = os.path.join(dirname, 'thumbnail.jpg') if not os.path.exists(pic): pic = os.path.join(dirname, 'imagecache', '00.png') if not os.path.exists(pic): pic = os.path.join(dirname, 'imagecache', '000.png') if os.path.exists(pic): with open(pic, 'rb') as f: vid.thumbnail.save(os.path.basename(pic), File(f)) if not vid.activity.thumbnail: # Use same thumbnail with open(pic, 'rb') as f: vid.activity.thumbnail.save(os.path.basename(pic), File(f)) vid.activity.save() if not vid.activity.chapter.thumbnail: # Use same thumbnail with open(pic, 'rb') as f: vid.activity.chapter.thumbnail.save(os.path.basename(pic), File(f)) vid.activity.chapter.save() if not vid.activity.chapter.channel.thumbnail: # Use same thumbnail with open(pic, 'rb') as f: vid.activity.chapter.channel.thumbnail.save(os.path.basename(pic), File(f)) vid.activity.chapter.channel.save() vid.save() # Read data.json if available packageurl = data.get('dataurl', os.path.join(dirname, 'data.json')) if packageurl.startswith('http') or os.path.exists(packageurl): self.stdout.write("Loading data from %s" % packageurl) f = urllib.urlopen(packageurl.replace('https', 'http')) try: package = json.loads("".join(f.readlines())) except ValueError: package = {} f.close() if not package: print "************* Empty package: %s" % packageurl return # Update video duration vid.duration = package['medias'][0]['meta']['dc:duration'] / 1000.0 slug = package['medias'][0]['id'] if re.match('^\d', slug): # id starting with a number. Add a "m" (media) prefix. slug = "m" + slug if Video.objects.filter(slug=slug).exists(): self.stdout.write("Duplicate video slug: " + slug) slug = slugify(title) else: vid.slug = slug vid.save() ats = {} for atjson in package['annotation-types']: sl = slugify(atjson['dc:title']) try: at = AnnotationType.objects.get(slug=sl) except AnnotationType.DoesNotExist: # Create the AnnotationType matching dc:title/slug at = AnnotationType(creator=adminuser, created=convert_date(dateutil.parser.parse(atjson['dc:created'])), title=atjson['dc:title'], slug=sl, description=atjson['dc:description']) at.save() ats[atjson['id']] = at if 'Notes' not in ats: at = AnnotationType.objects.get_or_create(title="Notes", slug=slugify("Notes"), defaults={ 'creator': adminuser, 'description': "User note" })[0] ats["Notes"] = at self.stdout.write("Copying %d annotations" % len(package['annotations'])) for a in package['annotations']: if a['meta']['id-ref'] == "at_contributions": at = ats['Notes'] else: at = ats[a['meta']['id-ref']] self.stdout.write(".", ending="") self.stdout.flush() tags = [] creator = get_user(a['meta']['dc:creator']) contributor = get_user(a['meta']['dc:contributor']) title = a['content']['title'] description = a['content']['description'] group = get_group(a['meta']['id-ref']) visibility = VISIBILITY_PRIVATE if group is not None: visibility = VISIBILITY_GROUP # Use "Notes" type since we now have the group information at = ats['Notes'] if at.title not in ('Quiz', 'QuizPerso', TYPE_SLIDES, 'Partie'): description = description or title title = "" if at.title in ('Quiz', TYPE_SLIDES, 'Partie'): visibility = VISIBILITY_PUBLIC creator = contributor = get_user('coco') if 'public' in at.title.lower(): visibility = VISIBILITY_PUBLIC at = ats['Notes'] m = re.match("^\[([\w-]+,)?(\w+)](.*)", description) if m: creator = contributor = get_user(m.group(2), context=a['meta']['id-ref']) if m.group(1) is not None: tags.append(m.group(1).strip(',').strip().lower()) description = m.group(3).strip() if group and not creator in group.user_set.all(): group.user_set.add(creator) an = Annotation(creator=creator, contributor=contributor, created=convert_date(dateutil.parser.parse(a['meta']['dc:created'])), modified=convert_date(dateutil.parser.parse(a['meta']['dc:modified'])), annotationtype=at, video=vid, begin=long(a['begin']) / 1000.0, end=long(a['end']) / 1000.0, title=title, visibility=visibility, group=group, description=description) # FIXME: handle tags if 'data' in a['content']: an.contenttype = a['content'].get('mimetype', 'application/json') an.contentdata = json.dumps(a['content']['data']) elif 'level' in a['content']: an.contenttype = 'application/json' an.contentdata = json.dumps({'level': a['content']['level']}) elif at.title == TYPE_SLIDES: # Enforce level 1 an.contenttype = 'application/json' an.contentdata = json.dumps({'level': 1}) if 'img' in a['content']: img = a['content']['img']['src'] if ('note.png' not in img and 'contribution.svg' not in img): pic = os.path.join(dirname, a['content']['img']['src']) if os.path.exists(pic): with open(pic, 'rb') as f: an.thumbnail.save(os.path.basename(pic), File(f)) an.save() for t in tags: an.tags.add(t.lower())