class Video(models.Model): """Central object in the system""" video_id = models.CharField(max_length=255, unique=True) title = models.CharField(max_length=2048, blank=True) description = models.TextField(blank=True) duration = models.PositiveIntegerField(null=True, blank=True) allow_community_edits = models.BooleanField() allow_video_urls_edit = models.BooleanField(default=True) writelock_time = models.DateTimeField(null=True, editable=False) writelock_session_key = models.CharField(max_length=255, editable=False) writelock_owner = models.ForeignKey(User, null=True, editable=False, related_name="writelock_owners") is_subtitled = models.BooleanField(default=False) was_subtitled = models.BooleanField(default=False, db_index=True) thumbnail = models.CharField(max_length=500, blank=True) s3_thumbnail = S3EnabledImageField(blank=True, upload_to='video/thumbnail/') edited = models.DateTimeField(null=True, editable=False) created = models.DateTimeField(auto_now_add=True) user = models.ForeignKey(User, null=True, blank=True) followers = models.ManyToManyField(User, blank=True, related_name='followed_videos') complete_date = models.DateTimeField(null=True, blank=True, editable=False) subtitles_fetched_count = models.IntegerField(default=0, db_index=True) widget_views_count = models.IntegerField(default=0, db_index=True) view_count = models.PositiveIntegerField(default=0, db_index=True) subtitles_fetched_counter = RedisSimpleField('video_id', changed_video_set) widget_views_counter = RedisSimpleField('video_id', changed_video_set) view_counter = RedisSimpleField('video_id', changed_video_set) # Denormalizing the subtitles(had_version) count, in order to get faster joins # updated from update_languages_count() languages_count = models.PositiveIntegerField(default=0, db_index=True) def __unicode__(self): title = self.title_display() if len(title) > 70: title = title[:70] + '...' return title def title_display(self): if self.title: return self.title try: url = self.videourl_set.all()[:1].get().url if not url: return 'No title' except models.ObjectDoesNotExist: return 'No title' url = url.strip('/') if url.startswith('http://'): url = url[7:] parts = url.split('/') if len(parts) > 1: title = '%s/.../%s' % (parts[0], parts[-1]) else: title = url if title > 35: title = title[:35] + '...' return title def update_view_counter(self): self.view_counter.incr() video_view_counter.incr() def update_subtitles_fetched(self, lang=None): self.subtitles_fetched_counter.incr() #Video.objects.filter(pk=self.pk).update(subtitles_fetched_count=models.F('subtitles_fetched_count')+1) update_subtitles_fetch_counter(self, lang) if lang: lang.subtitles_fetched_counter.incr() #SubtitleLanguage.objects.filter(pk=sub_lang.pk).update(subtitles_fetched_count=models.F('subtitles_fetched_count')+1) def get_thumbnail(self): if self.s3_thumbnail: return self.s3_thumbnail.thumb_url(100, 100) if self.thumbnail: return self.thumbnail return '' def get_small_thumbnail(self): if self.s3_thumbnail: return self.s3_thumbnail.thumb_url(50, 50) return '' @models.permalink def video_link(self): return ('videos:history', [self.video_id]) def thumbnail_link(self): if not self.thumbnail: return '' if self.thumbnail.startswith('http://'): return self.thumbnail return settings.MEDIA_URL + self.thumbnail def is_html5(self): try: return self.videourl_set.filter(original=True)[:1].get().is_html5() except models.ObjectDoesNotExist: return False def search_page_url(self): return self.get_absolute_url() def title_for_url(self): return self.title.replace('/', '-').replace('#', '').replace('?', '') @models.permalink def get_absolute_url(self, locale=None): kwargs = {} if locale: kwargs['locale'] = locale title = self.title_for_url() if title: return ('videos:video_with_title', [self.video_id, title], kwargs) return ('videos:video', [self.video_id], kwargs) def get_video_url(self): try: return self.videourl_set.filter( primary=True).all()[:1].get().effective_url except models.ObjectDoesNotExist: pass @classmethod def get_or_create_for_url(cls, video_url=None, vt=None, user=None): vt = vt or video_type_registrar.video_type_for_url(video_url) if not vt: return None, False try: video_url_obj = VideoUrl.objects.get(url=vt.convert_to_video_url()) return video_url_obj.video, False except models.ObjectDoesNotExist: pass try: video_url_obj = VideoUrl.objects.get(type=vt.abbreviation, **vt.create_kwars()) if user: Action.create_video_handler(video_url_obj.video, user) return video_url_obj.video, False except models.ObjectDoesNotExist: obj = Video() obj = vt.set_values(obj) if obj.title: obj.slug = slugify(obj.title) obj.user = user obj.save() user and obj.followers.add(user) Action.create_video_handler(obj, user) SubtitleLanguage(video=obj, is_original=True, is_forked=True).save() #Save video url video_url_obj = VideoUrl() if vt.video_id: video_url_obj.videoid = vt.video_id video_url_obj.url = vt.convert_to_video_url() video_url_obj.type = vt.abbreviation video_url_obj.original = True video_url_obj.primary = True video_url_obj.added_by = user video_url_obj.video = obj video_url_obj.save() return obj, True @property def language(self): ol = self._original_subtitle_language() if ol and ol.language: return ol.language @property def filename(self): from django.utils.text import get_valid_filename return get_valid_filename(self.__unicode__()) def lang_filename(self, language): name = self.filename lang = language.language or u'original' return u'%s.%s' % (name, lang) @property def subtitle_state(self): """Subtitling state for this video """ return NO_SUBTITLES if self.latest_version() \ is None else SUBTITLES_FINISHED def _original_subtitle_language(self): if not hasattr(self, '_original_subtitle'): try: original = self.subtitlelanguage_set.filter( is_original=True)[:1].get() except models.ObjectDoesNotExist: original = None setattr(self, '_original_subtitle', original) return getattr(self, '_original_subtitle') def has_original_language(self): original_language = self._original_subtitle_language() if original_language: return original_language.language != '' def subtitle_language(self, language_code=None): try: if language_code is None: return self._original_subtitle_language() else: return self.subtitlelanguage_set.filter( language=language_code).order_by( '-subtitle_count')[:1].get() except models.ObjectDoesNotExist: return None def subtitle_languages(self, language_code): return self.subtitlelanguage_set.filter(language=language_code) def version(self, version_no=None, language=None): if language is None: language = self.subtitle_language() return None if language is None else language.version(version_no) def latest_version(self, language_code=None): language = self.subtitle_language(language_code) return None if language is None else language.latest_version() def subtitles(self, version_no=None, language_code=None): version = self.version(version_no, language_code) if version: return version.subtitles() else: return Subtitle.objects.none() def update_languages_count(self): self.languages_count = self.subtitlelanguage_set.filter( had_version=True).count() self.save() def latest_subtitles(self, language_code=None): version = self.latest_version(language_code) return [] if version is None else version.subtitles() def translation_language_codes(self): """All iso language codes with finished translations.""" return set([ sl.language for sl in self.subtitlelanguage_set.filter( is_complete=True).filter(is_original=False) ]) @property def writelock_owner_name(self): """The user who currently has a subtitling writelock on this video.""" if self.writelock_owner == None: return "anonymous" else: return self.writelock_owner.__unicode__() @property def is_writelocked(self): """Is this video writelocked for subtitling?""" if self.writelock_time == None: return False delta = datetime.now() - self.writelock_time seconds = delta.days * 24 * 60 * 60 + delta.seconds return seconds < WRITELOCK_EXPIRATION def can_writelock(self, request): """Can I place a writelock on this video for subtitling?""" return self.writelock_session_key == \ request.browser_id or \ not self.is_writelocked def writelock(self, request): """Writelock this video for subtitling.""" self._make_writelock(request.user, request.browser_id) def _make_writelock(self, user, key): if user.is_authenticated(): self.writelock_owner = user else: self.writelock_owner = None self.writelock_session_key = key self.writelock_time = datetime.now() def release_writelock(self): """Writelock this video for subtitling.""" self.writelock_owner = None self.writelock_session_key = '' self.writelock_time = None def notification_list(self, exclude=None): qs = self.followers.exclude(changes_notification=False).exclude( is_active=False) if exclude: if not isinstance(exclude, (list, tuple)): exclude = [exclude] qs = qs.exclude( pk__in=[u.pk for u in exclude if u and u.is_authenticated()]) return qs def notification_list_all(self, exclude=None): users = [] for language in self.subtitlelanguage_set.all(): for u in language.notification_list(exclude): if not u in users: users.append(u) for user in self.notification_list(exclude): if not user in users: users.append(user) return users def update_complete_state(self): language = self.subtitle_language() if not language.has_version: self.is_subtitled = False else: self.is_subtitled = True self.was_subtitled = True def subtitle_language_dict(self): langs = {} for sl in self.subtitlelanguage_set.all(): if not sl.language: continue if sl.language in langs: langs[sl.language].append(sl) else: langs[sl.language] = [sl] return langs
class SubtitleLanguage(models.Model): video = models.ForeignKey(Video) is_original = models.BooleanField() language = models.CharField(max_length=16, choices=ALL_LANGUAGES, blank=True) writelock_time = models.DateTimeField(null=True, editable=False) writelock_session_key = models.CharField(max_length=255, blank=True, editable=False) writelock_owner = models.ForeignKey(User, null=True, blank=True, editable=False) is_complete = models.BooleanField(default=False) subtitle_count = models.IntegerField(default=0) has_version = models.BooleanField(default=False, editable=False) had_version = models.BooleanField(default=False, editable=False) is_forked = models.BooleanField(default=False, editable=False) created = models.DateTimeField(auto_now_add=True) subtitles_fetched_count = models.IntegerField(default=0, editable=False) followers = models.ManyToManyField(User, blank=True, related_name='followed_languages', editable=False) title = models.CharField(max_length=2048, blank=True) percent_done = models.IntegerField(default=0, editable=False) standard_language = models.ForeignKey('self', null=True, blank=True, editable=False) last_version = models.ForeignKey('SubtitleVersion', null=True, blank=True, editable=False) subtitles_fetched_counter = RedisSimpleField() class Meta: unique_together = (('video', 'language', 'standard_language'), ) def __unicode__(self): return self.language_display() def __init__(self, *args, **kwargs): super(SubtitleLanguage, self).__init__(*args, **kwargs) self.save_initial_values() def save(self, *args, **kwargs): super(SubtitleLanguage, self).save(*args, **kwargs) self.check_initial_values() self.save_initial_values() self.video.update_languages_count() def delete(self, *args, **kwargs): video_id = self.video_id super(SubtitleLanguage, self).delete(*args, **kwargs) #asynchronous call update_team_video.delay(video_id) if not self.video.subtitlelanguage_set.exclude( is_complete=False).exists(): self.video.complete_date = None self.video.save() video_cache.invalidate_cache(self.video.video_id) self.video.update_languages_count() def save_initial_values(self): self._initial_values = { 'is_complete': self.is_complete, 'is_original': self.is_original, 'percent_done': self.percent_done, 'is_forked': self.is_forked } def check_initial_values(self): iv = self._initial_values if not iv['is_complete'] and self.is_complete: self.video.complete_date = datetime.now() self.video.save() elif iv['is_complete'] and not self.is_complete: if not self.video.subtitlelanguage_set.exclude( is_complete=False).exists(): self.video.complete_date = None self.video.save() if iv['is_complete'] != self.is_complete or iv['is_original'] != self.is_original \ or iv['percent_done'] != self.percent_done or iv['is_forked'] != self.is_forked: #asynchronous call update_team_video_for_sl.delay(self.id) def get_title(self): if self.is_original: return self.video.title return self.title def update_complete_state(self): self._update_subtitle_count() version = self.latest_version() if version.subtitle_set.count() == 0: self.has_version = False else: self.has_version = True self.had_version = True def is_dependent(self): return not self.is_original and not self.is_forked def real_standard_language(self): if self.standard_language: return self.standard_language elif self.is_dependent(): # This should only be needed temporarily until data is more cleaned up. # in other words, self.standard_language should never be None for a dependent SL self.standard_language = self.video.subtitle_language() self.save() return self.standard_language return None def is_dependable(self): if self.is_dependent(): dep_lang = self.real_standard_language() return dep_lang and dep_lang.is_complete and self.percent_done > 10 else: return self.is_complete def get_widget_url(self): # duplicates mirosubs.widget.SubtitleDialogOpener.prototype.openDialogOrRedirect_ video = self.video video_url = video.get_video_url() config = { "videoID": video.video_id, "videoURL": video_url, "effectiveVideoURL": video_url, "languageCode": self.language, "subLanguagePK": self.pk, "originalLanguageCode": video.language } if self.is_dependent(): config['baseLanguagePK'] = self.standard_language.pk return reverse('onsite_widget') + '?config=' + urlquote_plus( json.dumps(config)) @models.permalink def get_absolute_url(self): if self.is_original: return ('videos:history', [self.video.video_id]) else: return ('videos:translation_history', [self.video.video_id, self.language, self.pk]) def language_display(self): if self.is_original and not self.language: return 'Original' return self.get_language_display() @property def writelock_owner_name(self): if self.writelock_owner == None: return "anonymous" else: return self.writelock_owner.__unicode__() @property def is_writelocked(self): if self.writelock_time == None: return False delta = datetime.now() - self.writelock_time seconds = delta.days * 24 * 60 * 60 + delta.seconds return seconds < WRITELOCK_EXPIRATION def can_writelock(self, request): return self.writelock_session_key == \ request.browser_id or \ not self.is_writelocked def writelock(self, request): if request.user.is_authenticated(): self.writelock_owner = request.user else: self.writelock_owner = None self.writelock_session_key = request.browser_id self.writelock_time = datetime.now() def release_writelock(self): self.writelock_owner = None self.writelock_session_key = '' self.writelock_time = None def version(self, version_no=None): if version_no is None: return self.latest_version() try: return self.subtitleversion_set.get(version_no=version_no) except models.ObjectDoesNotExist: pass def latest_version(self): #better get rid from this in future return self.last_version def latest_subtitles(self): version = self.latest_version() if version: return version.subtitles() return [] def _update_subtitle_count(self): original_value = self.subtitle_count new_value = len(self.latest_subtitles()) if original_value != new_value: self.subtitle_count = new_value self.save() def update_percent_done(self): original_value = self.percent_done if not self.is_original and not self.is_forked: try: translation_count = 0 for item in self.latest_subtitles(): if item.text: translation_count += 1 except AttributeError: translation_count = 0 if translation_count == 0: self.percent_done = 0 if self.standard_language and self.is_dependent(): last_version = self.standard_language.latest_version() else: last_version = self.video.latest_version() if last_version: subtitles_count = last_version.subtitle_set.count() else: subtitles_count = 0 try: val = int(translation_count / 1. / subtitles_count * 100) self.percent_done = max(0, min(val, 100)) except ZeroDivisionError: self.percent_done = 0 else: self.percent_done = 100 if original_value != self.percent_done: self.save() def notification_list(self, exclude=None): qs = self.followers.filter(changes_notification=True, is_active=True) if exclude: if not isinstance(exclude, (list, tuple)): exclude = [exclude] qs = qs.exclude(pk__in=[u.pk for u in exclude if u]) return qs