def create(self, validated_data): from . import views cover = validated_data.pop("cover", None) description = validated_data.get("description") artist = music_models.Artist.objects.create( attributed_to=validated_data["attributed_to"], name=validated_data["name"], content_category=validated_data["content_category"], attachment_cover=cover, ) common_utils.attach_content(artist, "description", description) if validated_data.get("tags", []): tags_models.set_tags(artist, *validated_data["tags"]) channel = models.Channel( artist=artist, attributed_to=validated_data["attributed_to"], metadata=validated_data["metadata"], ) channel.actor = models.generate_actor( validated_data["username"], name=validated_data["name"], ) channel.library = music_models.Library.objects.create( name=channel.actor.preferred_username, privacy_level="everyone", actor=validated_data["attributed_to"], ) channel.save() channel = views.ChannelViewSet.queryset.get(pk=channel.pk) return channel
def test_set_tags_honor_TAGS_MAX_BY_OBJ(factories, max, tags, expected, settings): settings.TAGS_MAX_BY_OBJ = max obj = factories["music.Artist"]() models.set_tags(obj, *tags) assert sorted(obj.tagged_items.values_list("tag__name", flat=True)) == expected
def create(self, validated_data): instance = models.Album.objects.create( attributed_to=self.context["user"].actor, artist=validated_data["artist"], release_date=validated_data.get("release_date"), title=validated_data["title"], attachment_cover=validated_data.get("cover"), ) common_utils.attach_content( instance, "description", validated_data.get("description") ) tag_models.set_tags(instance, *(validated_data.get("tags", []) or [])) return instance
def test_set_tags(factories, existing, given, expected): obj = factories["music.Artist"]() for tag in existing: factories["tags.TaggedItem"](content_object=obj, tag__name=tag) models.set_tags(obj, *given) tagged_items = models.TaggedItem.objects.all() assert tagged_items.count() == len(expected) for tag in expected: match = tagged_items.get(tag__name=tag) assert match.content_object == obj
def update_track_metadata(audio_metadata, track): serializer = metadata.TrackMetadataSerializer(data=audio_metadata) serializer.is_valid(raise_exception=True) new_data = serializer.validated_data to_update = [ ("track", track, lambda data: data), ("album", track.album, lambda data: data["album"]), ("artist", track.artist, lambda data: data["artists"][0]), ( "album_artist", track.album.artist if track.album else None, lambda data: data["album"]["artists"][0], ), ] for id, obj, data_getter in to_update: if not obj: continue obj_updated_fields = [] try: obj_data = data_getter(new_data) except IndexError: continue for field, config in UPDATE_CONFIG[id].items(): getter = config.get( "getter", lambda data, field: data[config.get("field", field)]) try: new_value = getter(obj_data, field) except KeyError: continue old_value = getattr(obj, field) if new_value == old_value: continue obj_updated_fields.append(field) setattr(obj, field, new_value) if obj_updated_fields: obj.save(update_fields=obj_updated_fields) tags_models.set_tags(track, *new_data.get("tags", [])) if track.album and "album" in new_data and new_data["album"].get( "cover_data"): common_utils.attach_file(track.album, "attachment_cover", new_data["album"].get("cover_data"))
def update(self, obj, validated_data): if validated_data.get("tags") is not None: tags_models.set_tags(obj.artist, *validated_data["tags"]) actor_update_fields = [] artist_update_fields = [] obj.metadata = validated_data["metadata"] obj.save(update_fields=["metadata"]) if "description" in validated_data: common_utils.attach_content( obj.artist, "description", validated_data["description"] ) if "name" in validated_data: actor_update_fields.append(("name", validated_data["name"])) artist_update_fields.append(("name", validated_data["name"])) if "content_category" in validated_data: artist_update_fields.append( ("content_category", validated_data["content_category"]) ) if "cover" in validated_data: artist_update_fields.append(("attachment_cover", validated_data["cover"])) if actor_update_fields: for field, value in actor_update_fields: setattr(obj.actor, field, value) obj.actor.save(update_fields=[f for f, _ in actor_update_fields]) if artist_update_fields: for field, value in artist_update_fields: setattr(obj.artist, field, value) obj.artist.save(update_fields=[f for f, _ in artist_update_fields]) return obj
def save(self, channel, existing_uploads=[], **track_defaults): validated_data = self.validated_data categories = validated_data.get("tags", {}) expected_uuid = uuid.uuid3( uuid.NAMESPACE_URL, "rss://{}-{}".format(channel.pk, validated_data["id"]) ) existing_upload = get_cached_upload(existing_uploads, expected_uuid) if existing_upload: existing_track = existing_upload.track else: existing_track = ( music_models.Track.objects.filter( uuid=expected_uuid, artist__channel=channel ) .select_related("description", "attachment_cover") .first() ) if existing_track: existing_upload = existing_track.uploads.filter( library=channel.library ).first() track_defaults = track_defaults track_defaults.update( { "disc_number": validated_data.get("itunes_season", 1) or 1, "position": validated_data.get("itunes_episode", 1) or 1, "title": validated_data["title"][ : music_models.MAX_LENGTHS["TRACK_TITLE"] ], "artist": channel.artist, } ) if "rights" in validated_data: track_defaults["copyright"] = validated_data["rights"][ : music_models.MAX_LENGTHS["COPYRIGHT"] ] if "published_parsed" in validated_data: track_defaults["creation_date"] = datetime.datetime.fromtimestamp( time.mktime(validated_data["published_parsed"]) ).replace(tzinfo=pytz.utc) upload_defaults = { "source": validated_data["links"]["audio"]["source"], "size": validated_data["links"]["audio"]["size"], "mimetype": validated_data["links"]["audio"]["mimetype"], "duration": validated_data.get("itunes_duration") or None, "import_status": "finished", "library": channel.library, } if existing_track: track_kwargs = {"pk": existing_track.pk} upload_kwargs = {"track": existing_track} else: track_kwargs = {"pk": None} track_defaults["uuid"] = expected_uuid upload_kwargs = {"pk": None} if existing_upload and existing_upload.source != upload_defaults["source"]: # delete existing upload, the url to the audio file has changed existing_upload.delete() # create/update the track track, created = music_models.Track.objects.update_or_create( **track_kwargs, defaults=track_defaults, ) # optimisation for reducing SQL queries, because we cannot use select_related with # update or create, so we restore the cache by hand if existing_track: for field in ["attachment_cover", "description"]: cached_id_value = getattr(existing_track, "{}_id".format(field)) new_id_value = getattr(track, "{}_id".format(field)) if new_id_value and cached_id_value == new_id_value: setattr(track, field, getattr(existing_track, field)) cover = validated_data.get("image") if cover: common_utils.attach_file(track, "attachment_cover", cover) tags = categories.get("tags", []) if tags: tags_models.set_tags(track, *tags) summary = validated_data.get("summary_detail") if summary: common_utils.attach_content(track, "description", summary) if created: upload_defaults["track"] = track # create/update the upload upload, created = music_models.Upload.objects.update_or_create( **upload_kwargs, defaults=upload_defaults ) return upload
def save(self, rss_url): validated_data = self.validated_data # because there may be redirections from the original feed URL real_rss_url = validated_data.get("atom_link", rss_url) or rss_url service_actor = actors.get_service_actor() author = validated_data.get("author_detail", {}) categories = validated_data.get("tags", {}) metadata = { "explicit": validated_data.get("itunes_explicit", False), "copyright": validated_data.get("rights"), "owner_name": author.get("name"), "owner_email": author.get("email"), "itunes_category": categories.get("parent"), "itunes_subcategory": categories.get("child"), "language": validated_data.get("language"), } public_url = validated_data["link"] existing = ( models.Channel.objects.external_rss() .filter( Q(rss_url=real_rss_url) | Q(rss_url=rss_url) | Q(actor__url=public_url) ) .first() ) channel_defaults = { "rss_url": real_rss_url, "metadata": metadata, } if existing: artist_kwargs = {"channel": existing} actor_kwargs = {"channel": existing} actor_defaults = {"url": public_url} else: artist_kwargs = {"pk": None} actor_kwargs = {"pk": None} preferred_username = "******".format(uuid.uuid4()) actor_defaults = { "preferred_username": preferred_username, "type": "Application", "domain": service_actor.domain, "url": public_url, "fid": federation_utils.full_url( reverse( "federation:actors-detail", kwargs={"preferred_username": preferred_username}, ) ), } channel_defaults["attributed_to"] = service_actor actor_defaults["last_fetch_date"] = timezone.now() # create/update the artist profile artist, created = music_models.Artist.objects.update_or_create( **artist_kwargs, defaults={ "attributed_to": service_actor, "name": validated_data["title"][ : music_models.MAX_LENGTHS["ARTIST_NAME"] ], "content_category": "podcast", }, ) cover = validated_data.get("image") if cover: common_utils.attach_file(artist, "attachment_cover", cover) tags = categories.get("tags", []) if tags: tags_models.set_tags(artist, *tags) summary = validated_data.get("summary_detail") if summary: common_utils.attach_content(artist, "description", summary) if created: channel_defaults["artist"] = artist # create/update the actor actor, created = federation_models.Actor.objects.update_or_create( **actor_kwargs, defaults=actor_defaults ) if created: channel_defaults["actor"] = actor # create the library if not existing: channel_defaults["library"] = music_models.Library.objects.create( actor=service_actor, privacy_level=settings.PODCASTS_THIRD_PARTY_VISIBILITY, name=actor_defaults["preferred_username"], ) # create/update the channel channel, created = models.Channel.objects.update_or_create( pk=existing.pk if existing else None, defaults=channel_defaults, ) return channel
def update(self, instance, validated_data): tags = validated_data.pop("tags", NOOP) r = super().update(instance, validated_data) if tags != NOOP: tags_models.set_tags(instance, *tags) return r