This commit is contained in:
Your Name 2024-03-10 20:55:50 -04:00 committed by Henri Dickson
parent 07e7dd2dd2
commit 4fb4e2834a
17 changed files with 284 additions and 245 deletions

View file

@ -1,6 +1,7 @@
import os
import environ
from django.utils.translation import gettext_lazy as _
from boofilsic import __version__
@ -34,7 +35,10 @@ env = environ.FileAwareEnv(
NEODB_USER_ICON=(str, "/s/img/avatar.svg"),
NEODB_SITE_INTRO=(str, ""),
NEODB_SITE_HEAD=(str, ""),
NEODB_SITE_DESCRIPTION=(str, "一个自由、开放、互联的书籍、电影、音乐和游戏收藏评论交流社区"),
NEODB_SITE_DESCRIPTION=(
str,
"reviews about book, film, music, podcast and game.",
),
# Links in site footer
NEODB_SITE_LINKS=(dict, {}),
# Invite only mode
@ -384,6 +388,11 @@ MARKDOWNX_MARKDOWNIFY_FUNCTION = "journal.models.render_md"
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = "zh-hans"
LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")]
# LANGUAGES = (
# ("en", _("English")),
# ("zh-hans", _("Simplified Chinese")),
# )
TIME_ZONE = env("NEODB_TIMEZONE", default="Asia/Shanghai") # type: ignore

View file

@ -61,7 +61,6 @@ class EditionSchema(EditionInSchema, BaseSchema):
class Edition(Item):
category = ItemCategory.Book
url_path = "book"
demonstrative = _("这本书")
isbn = PrimaryLookupIdDescriptor(IdType.ISBN)
asin = PrimaryLookupIdDescriptor(IdType.ASIN)
@ -88,53 +87,53 @@ class Edition(Item):
"contents",
]
subtitle = jsondata.CharField(
_("副标题"), null=True, blank=True, default=None, max_length=500
_("subtitle"), null=True, blank=True, default=None, max_length=500
)
orig_title = jsondata.CharField(
_("原名"), null=True, blank=True, default=None, max_length=500
_("original title"), null=True, blank=True, default=None, max_length=500
)
author = jsondata.ArrayField(
verbose_name=_("作者"),
verbose_name=_("author"),
base_field=models.CharField(max_length=500),
null=False,
blank=False,
default=list,
)
translator = jsondata.ArrayField(
verbose_name=_("译者"),
verbose_name=_("translator"),
base_field=models.CharField(max_length=500),
null=True,
blank=True,
default=list,
)
language = jsondata.CharField(
_("语言"), null=True, blank=True, default=None, max_length=500
_("language"), null=True, blank=True, default=None, max_length=500
)
pub_house = jsondata.CharField(
_("出版社"), null=True, blank=False, default=None, max_length=500
_("publisher"), null=True, blank=False, default=None, max_length=500
)
pub_year = jsondata.IntegerField(
_("出版年份"),
_("publication year"),
null=True,
blank=False,
validators=[MinValueValidator(1), MaxValueValidator(2999)],
)
pub_month = jsondata.IntegerField(
_("出版月份"),
_("publication month"),
null=True,
blank=True,
validators=[MinValueValidator(1), MaxValueValidator(12)],
)
binding = jsondata.CharField(
_("装订"), null=True, blank=True, default=None, max_length=500
_("binding"), null=True, blank=True, default=None, max_length=500
)
pages = jsondata.IntegerField(_("页数"), blank=True, default=None)
pages = jsondata.IntegerField(_("pages"), blank=True, default=None)
series = jsondata.CharField(
_("丛书"), null=True, blank=True, default=None, max_length=500
_("series"), null=True, blank=True, default=None, max_length=500
)
contents = jsondata.TextField(_("目录"), null=True, blank=True, default=None)
price = jsondata.CharField(_("价格"), null=True, blank=True, max_length=500)
imprint = jsondata.CharField(_("出品方"), null=True, blank=True, max_length=500)
contents = jsondata.TextField(_("contents"), null=True, blank=True, default=None)
price = jsondata.CharField(_("price"), null=True, blank=True, max_length=500)
imprint = jsondata.CharField(_("imprint"), null=True, blank=True, max_length=500)
@property
def isbn10(self):

View file

@ -244,7 +244,7 @@ class URLField(JSONFieldMixin, fields.URLField):
class ArrayField(JSONFieldMixin, DJANGO_ArrayField):
# def __init__(self, *args, **kwargs):
# kwargs["help_text"] = _("多项之间以英文逗号分隔")
# kwargs["help_text"] = _("comma separated list of values")
# super().__init__(*args, **kwargs)
def from_json(self, value): # backward compatible with dirty legacy data

View file

@ -22,19 +22,17 @@ from .mixins import SoftDeleteMixin
from .utils import DEFAULT_ITEM_COVER, item_cover_path, resource_cover_path
if TYPE_CHECKING:
from django.utils.functional import _StrOrPromise
from users.models import User
_logger = logging.getLogger(__name__)
class SiteName(models.TextChoices):
Unknown = "unknown", _("未知站点")
Douban = "douban", _("豆瓣")
Unknown = "unknown", _("Unknown")
Douban = "douban", _("Douban")
Goodreads = "goodreads", _("Goodreads")
GoogleBooks = "googlebooks", _("谷歌图书")
BooksTW = "bookstw", _("博客来")
GoogleBooks = "googlebooks", _("Google Books")
BooksTW = "bookstw", _("BooksTW")
IMDB = "imdb", _("IMDb")
TMDB = "tmdb", _("TMDB")
Bandcamp = "bandcamp", _("Bandcamp")
@ -43,57 +41,57 @@ class SiteName(models.TextChoices):
Steam = "steam", _("Steam")
Bangumi = "bangumi", _("Bangumi")
BGG = "bgg", _("BGG")
# ApplePodcast = "apple_podcast", _("苹果播客")
# ApplePodcast = "apple_podcast", _("Apple Podcast")
RSS = "rss", _("RSS")
Discogs = "discogs", _("Discogs")
AppleMusic = "apple_music", _("苹果音乐")
Fediverse = "fedi", _("联邦实例")
AppleMusic = "apple_music", _("Apple Music")
Fediverse = "fedi", _("Fediverse")
class IdType(models.TextChoices):
WikiData = "wikidata", _("维基数据")
WikiData = "wikidata", _("WikiData")
ISBN10 = "isbn10", _("ISBN10")
ISBN = "isbn", _("ISBN") # ISBN 13
ASIN = "asin", _("ASIN")
ISSN = "issn", _("ISSN")
CUBN = "cubn", _("统一书号")
CUBN = "cubn", _("CUBN")
ISRC = "isrc", _("ISRC") # only for songs
GTIN = "gtin", _("GTIN UPC EAN") # GTIN-13, ISBN is separate
GTIN = "gtin", _("GTIN UPC EAN") # GTIN-13, ISBN is separate
RSS = "rss", _("RSS Feed URL")
IMDB = "imdb", _("IMDb")
TMDB_TV = "tmdb_tv", _("TMDB剧集")
TMDB_TVSeason = "tmdb_tvseason", _("TMDB剧集")
TMDB_TVEpisode = "tmdb_tvepisode", _("TMDB剧集")
TMDB_Movie = "tmdb_movie", _("TMDB电影")
TMDB_TV = "tmdb_tv", _("TMDB TV Serie")
TMDB_TVSeason = "tmdb_tvseason", _("TMDB TV Season")
TMDB_TVEpisode = "tmdb_tvepisode", _("TMDB TV Episode")
TMDB_Movie = "tmdb_movie", _("TMDB Movie")
Goodreads = "goodreads", _("Goodreads")
Goodreads_Work = "goodreads_work", _("Goodreads著作")
GoogleBooks = "googlebooks", _("谷歌图书")
DoubanBook = "doubanbook", _("豆瓣读书")
DoubanBook_Work = "doubanbook_work", _("豆瓣读书著作")
DoubanMovie = "doubanmovie", _("豆瓣电影")
DoubanMusic = "doubanmusic", _("豆瓣音乐")
DoubanGame = "doubangame", _("豆瓣游戏")
DoubanDrama = "doubandrama", _("豆瓣舞台剧")
DoubanDramaVersion = "doubandrama_version", _("豆瓣舞台剧版本")
BooksTW = "bookstw", _("博客来图书")
Goodreads_Work = "goodreads_work", _("Goodreads Work")
GoogleBooks = "googlebooks", _("Google Books")
DoubanBook = "doubanbook", _("Douban Book")
DoubanBook_Work = "doubanbook_work", _("Douban Book Work")
DoubanMovie = "doubanmovie", _("Douban Movie")
DoubanMusic = "doubanmusic", _("Douban Music")
DoubanGame = "doubangame", _("Douban Game")
DoubanDrama = "doubandrama", _("Douban Drama")
DoubanDramaVersion = "doubandrama_version", _("Douban Drama Version")
BooksTW = "bookstw", _("BooksTW Book")
Bandcamp = "bandcamp", _("Bandcamp")
Spotify_Album = "spotify_album", _("Spotify专辑")
Spotify_Show = "spotify_show", _("Spotify播客")
Spotify_Album = "spotify_album", _("Spotify Album")
Spotify_Show = "spotify_show", _("Spotify Podcast")
Discogs_Release = "discogs_release", ("Discogs Release")
Discogs_Master = "discogs_master", ("Discogs Master")
MusicBrainz = "musicbrainz", ("MusicBrainz ID")
DoubanBook_Author = "doubanbook_author", _("豆瓣读书作者")
DoubanCelebrity = "doubanmovie_celebrity", _("豆瓣电影影人")
Goodreads_Author = "goodreads_author", _("Goodreads作者")
Spotify_Artist = "spotify_artist", _("Spotify艺术家")
TMDB_Person = "tmdb_person", _("TMDB影人")
IGDB = "igdb", _("IGDB游戏")
BGG = "bgg", _("BGG桌游")
Steam = "steam", _("Steam游戏")
# DoubanBook_Author = "doubanbook_author", _("豆瓣读书作者")
# DoubanCelebrity = "doubanmovie_celebrity", _("豆瓣电影影人")
# Goodreads_Author = "goodreads_author", _("Goodreads作者")
# Spotify_Artist = "spotify_artist", _("Spotify艺术家")
# TMDB_Person = "tmdb_person", _("TMDB影人")
IGDB = "igdb", _("IGDB Game")
BGG = "bgg", _("BGG Boardgame")
Steam = "steam", _("Steam Game")
Bangumi = "bangumi", _("Bangumi")
ApplePodcast = "apple_podcast", _("苹果播客")
AppleMusic = "apple_music", _("苹果音乐")
Fediverse = "fedi", _("联邦实例")
ApplePodcast = "apple_podcast", _("Apple Podcast")
AppleMusic = "apple_music", _("Apple Music")
Fediverse = "fedi", _("Fediverse")
IdealIdTypes = [
@ -109,46 +107,43 @@ IdealIdTypes = [
class ItemType(models.TextChoices):
Book = "book", _("")
TVShow = "tvshow", _("剧集")
TVSeason = "tvseason", _("剧集分季")
TVEpisode = "tvepisode", _("剧集分集")
Movie = "movie", _("电影")
Album = "music", _("音乐专辑")
Game = "game", _("游戏")
Podcast = "podcast", _("播客")
PodcastEpisode = "podcastepisode", _("播客单集")
FanFic = "fanfic", _("网文")
Performance = "performance", _("剧目")
PerformanceProduction = "production", _("上演")
Exhibition = "exhibition", _("展览")
Collection = "collection", _("收藏单")
Book = "book", _("Book")
TVShow = "tvshow", _("TV Serie")
TVSeason = "tvseason", _("TV Season")
TVEpisode = "tvepisode", _("TV Episode")
Movie = "movie", _("Movie")
Album = "music", _("Album")
Game = "game", _("Game")
Podcast = "podcast", _("Podcast Program")
PodcastEpisode = "podcastepisode", _("Podcast Episode")
Performance = "performance", _("Performance")
PerformanceProduction = "production", _("Production")
FanFic = "fanfic", _("Fanfix")
Exhibition = "exhibition", _("Exhibition")
Collection = "collection", _("Collection")
class ItemCategory(models.TextChoices):
Book = "book", _("")
Movie = "movie", _("电影")
TV = "tv", _("剧集")
Music = "music", _("音乐")
Game = "game", _("游戏")
Podcast = "podcast", _("播客")
FanFic = "fanfic", _("网文")
Performance = "performance", _("演出")
Exhibition = "exhibition", _("展览")
Collection = "collection", _("收藏单")
Book = "book", _("Book")
Movie = "movie", _("Movie")
TV = "tv", _("TV")
Music = "music", _("Music")
Game = "game", _("Game")
Podcast = "podcast", _("Podcast")
Performance = "performance", _("Performance")
FanFic = "fanfic", _("FanFic")
Exhibition = "exhibition", _("Exhibition")
Collection = "collection", _("Collection")
class AvailableItemCategory(models.TextChoices):
Book = "book", _("")
Movie = "movie", _("电影")
TV = "tv", _("剧集")
Music = "music", _("音乐")
Game = "game", _("游戏")
Podcast = "podcast", _("播客")
Performance = "performance", _("演出")
# FanFic = "fanfic", _("网文")
# Exhibition = "exhibition", _("展览")
# Collection = "collection", _("收藏单")
Book = "book", _("Book")
Movie = "movie", _("Movie")
TV = "tv", _("TV")
Music = "music", _("Music")
Game = "game", _("Game")
Podcast = "podcast", _("Podcast")
Performance = "performance", _("Performance")
# class SubItemType(models.TextChoices):
@ -258,19 +253,22 @@ class Item(SoftDeleteMixin, PolymorphicModel):
child_class = None # subclass may specify this to allow link to parent item
parent_class = None # subclass may specify this to allow create child item
category: ItemCategory # subclass must specify this
demonstrative: "_StrOrPromise | None" = None # subclass must specify this
uid = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)
title = models.CharField(_("标题"), max_length=1000, default="")
brief = models.TextField(_("简介"), blank=True, default="")
title = models.CharField(_("title"), max_length=1000, default="")
brief = models.TextField(_("description"), blank=True, default="")
primary_lookup_id_type = models.CharField(
_("主要标识类型"), blank=False, null=True, max_length=50
_("Primary ID Type"), blank=False, null=True, max_length=50
)
primary_lookup_id_value = models.CharField(
_("主要标识数值"), blank=False, null=True, max_length=1000
_("Primary ID Value"),
blank=False,
null=True,
max_length=1000,
help_text="automatically detected, usually no change necessary, left empty if unsure",
)
metadata = models.JSONField(_("其它信息"), blank=True, null=True, default=dict)
metadata = models.JSONField(_("metadata"), blank=True, null=True, default=dict)
cover = models.ImageField(
_("封面"), upload_to=item_cover_path, default=DEFAULT_ITEM_COVER, blank=True
_("cover"), upload_to=item_cover_path, default=DEFAULT_ITEM_COVER, blank=True
)
created_time = models.DateTimeField(auto_now_add=True)
edited_time = models.DateTimeField(auto_now=True)
@ -552,10 +550,12 @@ class ItemLookupId(models.Model):
Item, null=True, on_delete=models.SET_NULL, related_name="lookup_ids"
)
id_type = models.CharField(
_("源网站"), blank=True, choices=IdType.choices, max_length=50
_("source site"), blank=True, choices=IdType.choices, max_length=50
)
id_value = models.CharField(_("ID on source site"), blank=True, max_length=1000)
raw_url = models.CharField(
_("source url"), blank=True, max_length=1000, unique=True
)
id_value = models.CharField(_("源网站ID"), blank=True, max_length=1000)
raw_url = models.CharField(_("源网站ID"), blank=True, max_length=1000, unique=True)
class Meta:
unique_together = [["id_type", "id_value"]]

View file

@ -23,10 +23,13 @@ def _EditForm(item_model):
primary_lookup_id_type = forms.ChoiceField(
required=False,
choices=item_model.lookup_id_type_choices(),
label=_("主要标识类型"),
label=_("Primary ID Type"),
help_text="automatically detected, usually no change necessary",
)
primary_lookup_id_value = forms.CharField(
required=False, label=_("主要标识数据通常由系统自动检测,请勿随意更改现有值,新增条目不确定留空即可")
required=False,
label=_("Primary ID Value"),
help_text="automatically detected, usually no change necessary, left empty if unsure",
)
class Meta:

View file

@ -34,7 +34,6 @@ class Game(Item):
type = ItemType.Game
category = ItemCategory.Game
url_path = "game"
demonstrative = _("这个游戏")
igdb = PrimaryLookupIdDescriptor(IdType.IGDB)
steam = PrimaryLookupIdDescriptor(IdType.Steam)
douban_game = PrimaryLookupIdDescriptor(IdType.DoubanGame)
@ -56,7 +55,7 @@ class Game(Item):
other_title = jsondata.ArrayField(
base_field=models.CharField(blank=True, default="", max_length=500),
verbose_name=_("其它标题"),
verbose_name=_("other title"),
null=True,
blank=True,
default=list,
@ -64,7 +63,7 @@ class Game(Item):
designer = jsondata.ArrayField(
base_field=models.CharField(blank=True, default="", max_length=500),
verbose_name=_("设计者"),
verbose_name=_("designer"),
null=True,
blank=True,
default=list,
@ -72,7 +71,7 @@ class Game(Item):
artist = jsondata.ArrayField(
base_field=models.CharField(blank=True, default="", max_length=500),
verbose_name=_("艺术家"),
verbose_name=_("artist"),
null=True,
blank=True,
default=list,
@ -80,7 +79,7 @@ class Game(Item):
developer = jsondata.ArrayField(
base_field=models.CharField(blank=True, default="", max_length=500),
verbose_name=_("开发商"),
verbose_name=_("developer"),
null=True,
blank=True,
default=list,
@ -88,16 +87,18 @@ class Game(Item):
publisher = jsondata.ArrayField(
base_field=models.CharField(blank=True, default="", max_length=500),
verbose_name=_("发行商"),
verbose_name=_("publisher"),
null=True,
blank=True,
default=list,
)
release_year = jsondata.IntegerField(verbose_name=_("发布年份"), null=True, blank=True)
release_year = jsondata.IntegerField(
verbose_name=_("year of publication"), null=True, blank=True
)
release_date = jsondata.DateField(
verbose_name=_("发布日期"),
verbose_name=_("date of publication"),
auto_now=False,
auto_now_add=False,
null=True,
@ -106,7 +107,7 @@ class Game(Item):
)
genre = jsondata.ArrayField(
verbose_name=_("类型"),
verbose_name=_("genre"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
@ -114,13 +115,13 @@ class Game(Item):
)
platform = jsondata.ArrayField(
verbose_name=_("平台"),
verbose_name=_("platform"),
base_field=models.CharField(blank=True, default="", max_length=200),
default=list,
)
official_site = jsondata.CharField(
verbose_name=_("官方网站"), max_length=1000, null=True, blank=True
verbose_name=_("website"), max_length=1000, null=True, blank=True
)
@classmethod

View file

@ -3,6 +3,7 @@ from datetime import timedelta
from django.core.cache import cache
from django.db.models import Count, F
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from loguru import logger
from catalog.models import *
@ -87,7 +88,7 @@ class DiscoverGenerator(BaseJob):
{
"name": "popular_" + category.value,
"title": ""
+ (category.label if category != ItemCategory.Book else "图书"),
+ (category.label if category != ItemCategory.Book else _("Book")),
"items": items,
}
)

View file

@ -41,7 +41,6 @@ class Movie(Item):
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
tmdb_movie = PrimaryLookupIdDescriptor(IdType.TMDB_Movie)
douban_movie = PrimaryLookupIdDescriptor(IdType.DoubanMovie)
demonstrative = _("这部电影")
METADATA_COPY_LIST = [
"title",
@ -63,45 +62,45 @@ class Movie(Item):
"brief",
]
orig_title = jsondata.CharField(
verbose_name=_("原始标题"), blank=True, default="", max_length=500
verbose_name=_("original title"), blank=True, default="", max_length=500
)
other_title = jsondata.ArrayField(
verbose_name=_("其它标题"),
verbose_name=_("other title"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
default=list,
)
director = jsondata.ArrayField(
verbose_name=_("导演"),
verbose_name=_("director"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
default=list,
)
playwright = jsondata.ArrayField(
verbose_name=_("编剧"),
verbose_name=_("playwright"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
default=list,
)
actor = jsondata.ArrayField(
verbose_name=_("演员"),
verbose_name=_("actor"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
default=list,
)
genre = jsondata.ArrayField(
verbose_name=_("类型"),
verbose_name=_("genre"),
base_field=models.CharField(blank=True, default="", max_length=50),
null=True,
blank=True,
default=list,
) # , choices=MovieGenreEnum.choices
showtime = jsondata.JSONField(
_("上映日期"),
_("release date"),
null=True,
blank=True,
default=list,
@ -113,13 +112,15 @@ class Movie(Item):
"keys": {
"time": {
"type": "string",
"title": _("日期"),
"placeholder": _("必填"),
"title": _("date"),
"placeholder": _("required"),
},
"region": {
"type": "string",
"title": _("区域或类型"),
"placeholder": _("如中国大陆或柏林电影节"),
"title": _("region or event"),
"placeholder": _(
"Germany or Toronto International Film Festival"
),
},
},
"required": ["time"],
@ -127,10 +128,10 @@ class Movie(Item):
},
)
site = jsondata.URLField(
verbose_name=_("官方网站"), blank=True, default="", max_length=200
verbose_name=_("website"), blank=True, default="", max_length=200
)
area = jsondata.ArrayField(
verbose_name=_("国家地区"),
verbose_name=_("region"),
base_field=models.CharField(
blank=True,
default="",
@ -141,7 +142,7 @@ class Movie(Item):
default=list,
)
language = jsondata.ArrayField(
verbose_name=_("语言"),
verbose_name=_("language"),
base_field=models.CharField(
blank=True,
default="",
@ -151,9 +152,9 @@ class Movie(Item):
blank=True,
default=list,
)
year = jsondata.IntegerField(verbose_name=_("年份"), null=True, blank=True)
year = jsondata.IntegerField(verbose_name=_("year"), null=True, blank=True)
duration = jsondata.CharField(
verbose_name=_("片长"), blank=True, default="", max_length=200
verbose_name=_("length"), blank=True, default="", max_length=200
)
season_number = jsondata.IntegerField(
null=True, blank=True

View file

@ -36,7 +36,6 @@ class Album(Item):
type = ItemType.Album
url_path = "album"
category = ItemCategory.Music
demonstrative = _("这张专辑")
barcode = PrimaryLookupIdDescriptor(IdType.GTIN)
douban_music = PrimaryLookupIdDescriptor(IdType.DoubanMusic)
spotify_album = PrimaryLookupIdDescriptor(IdType.Spotify_Album)
@ -56,16 +55,18 @@ class Album(Item):
"bandcamp_album_id",
]
release_date = jsondata.DateField(
_("发行日期"), null=True, blank=True, help_text=_("YYYY-MM-DD")
_("release date"), null=True, blank=True, help_text=_("YYYY-MM-DD")
)
duration = jsondata.IntegerField(
_("length"), null=True, blank=True, help_text=_("milliseconds")
)
duration = jsondata.IntegerField(_("时长"), null=True, blank=True, help_text=_("毫秒数"))
artist = jsondata.ArrayField(
models.CharField(blank=True, default="", max_length=200),
verbose_name=_("艺术家"),
verbose_name=_("artist"),
default=list,
)
genre = jsondata.ArrayField(
verbose_name=_("流派"),
verbose_name=_("genre"),
base_field=models.CharField(blank=True, default="", max_length=50),
null=True,
blank=True,
@ -73,23 +74,27 @@ class Album(Item):
)
company = jsondata.ArrayField(
models.CharField(blank=True, default="", max_length=500),
verbose_name=_("发行方"),
verbose_name=_("publisher"),
null=True,
blank=True,
default=list,
)
track_list = jsondata.TextField(_("曲目"), blank=True, default="")
track_list = jsondata.TextField(_("tracks"), blank=True, default="")
other_title = jsondata.ArrayField(
verbose_name=_("其它标题"),
verbose_name=_("other title"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
default=list,
)
album_type = jsondata.CharField(_("专辑类型"), blank=True, default="", max_length=500)
media = jsondata.CharField(_("介质"), blank=True, default="", max_length=500)
album_type = jsondata.CharField(
_("album type"), blank=True, default="", max_length=500
)
media = jsondata.CharField(_("media type"), blank=True, default="", max_length=500)
bandcamp_album_id = jsondata.CharField(blank=True, default="", max_length=500)
disc_count = jsondata.IntegerField(_("碟片数"), blank=True, default="", max_length=500)
disc_count = jsondata.IntegerField(
_("number of disc"), blank=True, default="", max_length=500
)
def get_embed_link(self):
for res in self.external_resources.all():

View file

@ -4,8 +4,15 @@ from django.db import models
from django.utils.translation import gettext_lazy as _
from ninja import Schema
from catalog.common import *
from catalog.common.models import ItemSchema
from catalog.common import (
ExternalResource,
IdType,
Item,
ItemCategory,
ItemSchema,
ItemType,
jsondata,
)
from catalog.common.utils import DEFAULT_ITEM_COVER
@ -54,8 +61,8 @@ _CREW_SCHEMA = {
"items": {
"type": "dict",
"keys": {
"name": {"type": "string", "title": _("名字")},
"role": {"type": "string", "title": _("职能")},
"name": {"type": "string", "title": _("name")},
"role": {"type": "string", "title": _("role")},
},
"required": ["role", "name"],
},
@ -67,8 +74,16 @@ _ACTOR_SCHEMA = {
"items": {
"type": "dict",
"keys": {
"name": {"type": "string", "title": _("名字"), "placeholder": _("演员名字,必填")},
"role": {"type": "string", "title": _("角色"), "placeholder": _("也可不填写")},
"name": {
"type": "string",
"title": _("name"),
"placeholder": _("required"),
},
"role": {
"type": "string",
"title": _("role"),
"placeholder": _("optional"),
},
},
"required": ["name"],
},
@ -89,109 +104,108 @@ class Performance(Item):
child_class = "PerformanceProduction"
category = ItemCategory.Performance
url_path = "performance"
demonstrative = _("这部剧作")
orig_title = jsondata.CharField(
verbose_name=_("原名"), blank=True, default="", max_length=500
verbose_name=_("original name"), blank=True, default="", max_length=500
)
other_title = jsondata.ArrayField(
verbose_name=_("其它标题"),
verbose_name=_("other title"),
base_field=models.CharField(blank=False, default="", max_length=200),
null=False,
blank=True,
default=list,
)
genre = jsondata.ArrayField(
verbose_name=_("类型"),
verbose_name=_("genre"),
base_field=models.CharField(blank=False, default="", max_length=200),
null=False,
blank=False,
default=list,
)
language = jsondata.ArrayField(
verbose_name=_("语言"),
verbose_name=_("language"),
base_field=models.CharField(blank=False, default="", max_length=200),
null=False,
blank=True,
default=list,
)
director = jsondata.ArrayField(
verbose_name=_("导演"),
verbose_name=_("director"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
playwright = jsondata.ArrayField(
verbose_name=_("编剧"),
verbose_name=_("playwright"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
orig_creator = jsondata.ArrayField(
verbose_name=_("原作者"),
verbose_name=_("original creator"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
composer = jsondata.ArrayField(
verbose_name=_("作曲"),
verbose_name=_("composer"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
choreographer = jsondata.ArrayField(
verbose_name=_("编舞"),
verbose_name=_("choreographer"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
actor = jsondata.JSONField(
verbose_name=_("演员"),
verbose_name=_("actor"),
null=False,
blank=True,
default=list,
schema=_ACTOR_SCHEMA,
)
performer = jsondata.ArrayField(
verbose_name=_("表演者"),
verbose_name=_("performer"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
troupe = jsondata.ArrayField(
verbose_name=_("剧团"),
verbose_name=_("troupe"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
crew = jsondata.JSONField(
verbose_name=_("其他演职人员和团体"),
verbose_name=_("crew"),
null=False,
blank=True,
default=list,
schema=_CREW_SCHEMA,
)
location = jsondata.ArrayField(
verbose_name=_("剧场空间"),
verbose_name=_("theater"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
opening_date = jsondata.CharField(
verbose_name=_("首演日期"), max_length=100, null=True, blank=True
verbose_name=_("opening date"), max_length=100, null=True, blank=True
)
closing_date = jsondata.CharField(
verbose_name=_("结束日期"), max_length=100, null=True, blank=True
verbose_name=_("closing date"), max_length=100, null=True, blank=True
)
official_site = jsondata.CharField(
verbose_name=_("官方网站"), max_length=1000, null=True, blank=True
verbose_name=_("website"), max_length=1000, null=True, blank=True
)
METADATA_COPY_LIST = [
"title",
@ -236,105 +250,104 @@ class PerformanceProduction(Item):
type = ItemType.PerformanceProduction
category = ItemCategory.Performance
url_path = "performance/production"
demonstrative = _("这次上演")
show = models.ForeignKey(
Performance, null=True, on_delete=models.SET_NULL, related_name="productions"
)
orig_title = jsondata.CharField(
verbose_name=_("原名"), blank=True, default="", max_length=500
verbose_name=_("original title"), blank=True, default="", max_length=500
)
other_title = jsondata.ArrayField(
verbose_name=_("其它标题"),
verbose_name=_("other title"),
base_field=models.CharField(blank=False, default="", max_length=200),
null=False,
blank=True,
default=list,
)
language = jsondata.ArrayField(
verbose_name=_("语言"),
verbose_name=_("language"),
base_field=models.CharField(blank=False, default="", max_length=200),
null=False,
blank=True,
default=list,
)
director = jsondata.ArrayField(
verbose_name=_("导演"),
verbose_name=_("director"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
playwright = jsondata.ArrayField(
verbose_name=_("编剧"),
verbose_name=_("playwright"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
orig_creator = jsondata.ArrayField(
verbose_name=_("原作者"),
verbose_name=_("original creator"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
composer = jsondata.ArrayField(
verbose_name=_("作曲"),
verbose_name=_("composer"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
choreographer = jsondata.ArrayField(
verbose_name=_("编舞"),
verbose_name=_("choreographer"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
actor = jsondata.JSONField(
verbose_name=_("演员"),
verbose_name=_("actor"),
null=False,
blank=True,
default=list,
schema=_ACTOR_SCHEMA,
)
performer = jsondata.ArrayField(
verbose_name=_("表演者"),
verbose_name=_("performer"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
troupe = jsondata.ArrayField(
verbose_name=_("剧团"),
verbose_name=_("troupe"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
crew = jsondata.JSONField(
verbose_name=_("其他演职人员和团体"),
verbose_name=_("crew"),
null=False,
blank=True,
default=list,
schema=_CREW_SCHEMA,
)
location = jsondata.ArrayField(
verbose_name=_("剧场空间"),
verbose_name=_("theater"),
base_field=models.CharField(blank=False, default="", max_length=500),
null=False,
blank=True,
default=list,
)
opening_date = jsondata.CharField(
verbose_name=_("首演日期"), max_length=100, null=True, blank=False
verbose_name=_("opening date"), max_length=100, null=True, blank=False
)
closing_date = jsondata.CharField(
verbose_name=_("结束日期"), max_length=100, null=True, blank=True
verbose_name=_("closing date"), max_length=100, null=True, blank=True
)
official_site = jsondata.CharField(
verbose_name=_("官方网站"), max_length=1000, null=True, blank=True
verbose_name=_("website"), max_length=1000, null=True, blank=True
)
METADATA_COPY_LIST = [
"title",

View file

@ -29,12 +29,11 @@ class Podcast(Item):
category = ItemCategory.Podcast
child_class = "PodcastEpisode"
url_path = "podcast"
demonstrative = _("这档播客")
# apple_podcast = PrimaryLookupIdDescriptor(IdType.ApplePodcast)
# ximalaya = LookupIdDescriptor(IdType.Ximalaya)
# xiaoyuzhou = LookupIdDescriptor(IdType.Xiaoyuzhou)
genre = jsondata.ArrayField(
verbose_name=_("类型"),
verbose_name=_("genre"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
@ -42,13 +41,13 @@ class Podcast(Item):
)
hosts = jsondata.ArrayField(
verbose_name=_("主播"),
verbose_name=_("host"),
base_field=models.CharField(blank=True, default="", max_length=200),
default=list,
)
official_site = jsondata.CharField(
verbose_name=_("官方网站"), max_length=1000, null=True, blank=True
verbose_name=_("website"), max_length=1000, null=True, blank=True
)
METADATA_COPY_LIST = [
@ -87,12 +86,11 @@ class Podcast(Item):
class PodcastEpisode(Item):
category = ItemCategory.Podcast
url_path = "podcast/episode"
demonstrative = _("这集节目")
# uid = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)
program = models.ForeignKey(Podcast, models.CASCADE, related_name="episodes")
guid = models.CharField(null=True, max_length=1000)
pub_date = models.DateTimeField(
verbose_name=_("发布时间"), help_text="yyyy/mm/dd hh:mm"
verbose_name=_("date of publication"), help_text="yyyy/mm/dd hh:mm"
)
media_url = models.CharField(null=True, max_length=1000)
# title = models.CharField(default="", max_length=1000)

View file

@ -141,7 +141,7 @@
<div class="item-edit">
<span class="action inline">
<a href="{% url 'catalog:edit' item.url_path item.uuid %}"
title="{% trans '编辑' %}{{ item.demonstrative }}"><i class="fa-solid fa-pen-to-square"></i></a>
title="{% trans '编辑这个条目' %}"><i class="fa-solid fa-pen-to-square"></i></a>
</span>
{% if item.last_editor and item.last_editor.preference.show_last_edit %}
<span>

View file

@ -94,13 +94,14 @@ class TVShow(Item):
child_class = "TVSeason"
category = ItemCategory.TV
url_path = "tv"
demonstrative = _("这部剧集")
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
tmdb_tv = PrimaryLookupIdDescriptor(IdType.TMDB_TV)
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
season_count = models.IntegerField(verbose_name=_("总季数"), null=True, blank=True)
season_count = models.IntegerField(
verbose_name=_("number of seasons"), null=True, blank=True
)
episode_count = models.PositiveIntegerField(
verbose_name=_("总集数"), null=True, blank=True
verbose_name=_("number of episodes"), null=True, blank=True
)
METADATA_COPY_LIST = [
@ -123,45 +124,45 @@ class TVShow(Item):
"single_episode_length",
]
orig_title = jsondata.CharField(
verbose_name=_("原始标题"), blank=True, default="", max_length=500
verbose_name=_("original title"), blank=True, default="", max_length=500
)
other_title = jsondata.ArrayField(
base_field=models.CharField(blank=True, default="", max_length=500),
verbose_name=_("其它标题"),
verbose_name=_("other title"),
null=True,
blank=True,
default=list,
)
director = jsondata.ArrayField(
verbose_name=_("导演"),
verbose_name=_("director"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
default=list,
)
playwright = jsondata.ArrayField(
verbose_name=_("编剧"),
verbose_name=_("playwright"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
default=list,
)
actor = jsondata.ArrayField(
verbose_name=_("演员"),
verbose_name=_("actor"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
default=list,
)
genre = jsondata.ArrayField(
verbose_name=_("类型"),
verbose_name=_("genre"),
base_field=models.CharField(blank=True, default="", max_length=50),
null=True,
blank=True,
default=list,
) # , choices=MovieGenreEnum.choices
showtime = jsondata.JSONField(
_("播出日期"),
_("show time"),
null=True,
blank=True,
default=list,
@ -173,13 +174,15 @@ class TVShow(Item):
"keys": {
"time": {
"type": "string",
"title": _("日期"),
"placeholder": _("必填"),
"title": _("Date"),
"placeholder": _("YYYY-MM-DD"),
},
"region": {
"type": "string",
"title": _("区域或类型"),
"placeholder": _("如中国大陆或柏林电影节"),
"title": _("Region or Event"),
"placeholder": _(
"Germany or Toronto International Film Festival"
),
},
},
"required": ["time"],
@ -187,10 +190,10 @@ class TVShow(Item):
},
)
site = jsondata.URLField(
verbose_name=_("官方网站"), blank=True, default="", max_length=200
verbose_name=_("website"), blank=True, default="", max_length=200
)
area = jsondata.ArrayField(
verbose_name=_("国家地区"),
verbose_name=_("region"),
base_field=models.CharField(
blank=True,
default="",
@ -201,7 +204,7 @@ class TVShow(Item):
default=list,
)
language = jsondata.ArrayField(
verbose_name=_("语言"),
verbose_name=_("language"),
base_field=models.CharField(
blank=True,
default="",
@ -211,9 +214,9 @@ class TVShow(Item):
blank=True,
default=list,
)
year = jsondata.IntegerField(verbose_name=_("年份"), null=True, blank=True)
year = jsondata.IntegerField(verbose_name=_("year"), null=True, blank=True)
single_episode_length = jsondata.IntegerField(
verbose_name=_("单集长度"), null=True, blank=True
verbose_name=_("episode length"), null=True, blank=True
)
season_number = jsondata.IntegerField(
null=True, blank=True
@ -249,7 +252,6 @@ class TVSeason(Item):
type = ItemType.TVSeason
category = ItemCategory.TV
url_path = "tv/season"
demonstrative = _("这季剧集")
child_class = "TVEpisode"
douban_movie = PrimaryLookupIdDescriptor(IdType.DoubanMovie)
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
@ -257,8 +259,12 @@ class TVSeason(Item):
show = models.ForeignKey(
TVShow, null=True, on_delete=models.SET_NULL, related_name="seasons"
)
season_number = models.PositiveIntegerField(verbose_name=_("本季序号"), null=True)
episode_count = models.PositiveIntegerField(verbose_name=_("本季集数"), null=True)
season_number = models.PositiveIntegerField(
verbose_name=_("season number"), null=True
)
episode_count = models.PositiveIntegerField(
verbose_name=_("number of episodes"), null=True
)
METADATA_COPY_LIST = [
"title",
@ -280,45 +286,45 @@ class TVSeason(Item):
"brief",
]
orig_title = jsondata.CharField(
verbose_name=_("原始标题"), blank=True, default="", max_length=500
verbose_name=_("original title"), blank=True, default="", max_length=500
)
other_title = jsondata.ArrayField(
verbose_name=_("其它标题"),
verbose_name=_("other title"),
base_field=models.CharField(blank=True, default="", max_length=500),
null=True,
blank=True,
default=list,
)
director = jsondata.ArrayField(
verbose_name=_("导演"),
verbose_name=_("director"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
default=list,
)
playwright = jsondata.ArrayField(
verbose_name=_("编剧"),
verbose_name=_("playwright"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
default=list,
)
actor = jsondata.ArrayField(
verbose_name=_("演员"),
verbose_name=_("actor"),
base_field=models.CharField(blank=True, default="", max_length=200),
null=True,
blank=True,
default=list,
)
genre = jsondata.ArrayField(
verbose_name=_("类型"),
verbose_name=_("genre"),
base_field=models.CharField(blank=True, default="", max_length=50),
null=True,
blank=True,
default=list,
) # , choices=MovieGenreEnum.choices
showtime = jsondata.JSONField(
_("播出日期"),
_("show time"),
null=True,
blank=True,
default=list,
@ -330,13 +336,15 @@ class TVSeason(Item):
"keys": {
"time": {
"type": "string",
"title": _("日期"),
"placeholder": _("必填"),
"title": _("date"),
"placeholder": _("required"),
},
"region": {
"type": "string",
"title": _("区域或类型"),
"placeholder": _("如中国大陆或柏林电影节"),
"title": _("region or event"),
"placeholder": _(
"Germany or Toronto International Film Festival"
),
},
},
"required": ["time"],
@ -344,10 +352,10 @@ class TVSeason(Item):
},
)
site = jsondata.URLField(
verbose_name=_("官方网站"), blank=True, default="", max_length=200
verbose_name=_("website"), blank=True, default="", max_length=200
)
area = jsondata.ArrayField(
verbose_name=_("国家地区"),
verbose_name=_("region"),
base_field=models.CharField(
blank=True,
default="",
@ -358,7 +366,7 @@ class TVSeason(Item):
default=list,
)
language = jsondata.ArrayField(
verbose_name=_("语言"),
verbose_name=_("language"),
base_field=models.CharField(
blank=True,
default="",
@ -368,9 +376,9 @@ class TVSeason(Item):
blank=True,
default=list,
)
year = jsondata.IntegerField(verbose_name=_("年份"), null=True, blank=True)
year = jsondata.IntegerField(verbose_name=_("year"), null=True, blank=True)
single_episode_length = jsondata.IntegerField(
verbose_name=_("单集长度"), null=True, blank=True
verbose_name=_("episode length"), null=True, blank=True
)
duration = jsondata.CharField(
blank=True, default="", max_length=200

View file

@ -234,7 +234,7 @@ def fetch_tvepisodes(request, item_path, item_uuid):
django_rq.get_queue("crawl").enqueue(
fetch_episodes_for_season_task, item.uuid, request.user
)
messages.add_message(request, messages.INFO, _("已开始更新单集信息"))
messages.add_message(request, messages.INFO, _("Updating episodes"))
return redirect(item.url)
@ -257,10 +257,11 @@ def merge(request, item_path, item_uuid):
if request.POST.get("new_item_url"):
new_item = Item.get_by_url(request.POST.get("new_item_url"))
if not new_item or new_item.is_deleted or new_item.merged_to_item_id:
raise BadRequest(_("不能合并到一个被删除或合并过的条目。"))
raise BadRequest(_("Cannot be merged to an item already deleted or merged"))
if new_item.class_name != item.class_name:
raise BadRequest(
_("不能合并不同类的条目") + f" ({item.class_name} to {new_item.class_name})"
_("Cannot merge items in different categories")
+ f" ({item.class_name} to {new_item.class_name})"
)
_logger.warn(f"{request.user} merges {item} to {new_item}")
item.merge_to(new_item)

View file

@ -28,7 +28,7 @@ def duration_format(value, unit):
s = duration % 60
return f"{h}:{m:02}:{s:02}" if h else f"{m:02}:{s:02}"
# return (f"{h}小时 " if h else "") + (f"{m}分钟" if m else "")
except:
except Exception:
return f"{value} (format error)"

View file

@ -39,10 +39,10 @@ def current_user_relationship(context, target_identity: "APIdentity"):
r["following"] = current_identity.is_following(target_identity)
if r["following"]:
if current_identity.is_followed_by(target_identity):
r["status"] = _("互相关注")
r["status"] = _("mutual followed")
else:
r["status"] = _("已关注")
r["status"] = _("followed")
else:
if current_identity.is_followed_by(target_identity):
r["status"] = _("被ta关注")
r["status"] = _("following you")
return r

View file

@ -15,14 +15,14 @@ class ReviewForm(forms.ModelForm):
"item": forms.TextInput(attrs={"hidden": ""}),
}
title = forms.CharField(label=_("评论标题"))
body = MarkdownxFormField(label=_("评论正文 (Markdown格式可参考下方语法范例)"), strip=False)
title = forms.CharField(label=_("Title"))
body = MarkdownxFormField(label=_("Content (Markdown)"), strip=False)
share_to_mastodon = forms.BooleanField(
label=_("分享到联邦宇宙"), initial=False, required=False
label=_("Post to Fediverse"), initial=False, required=False
)
id = forms.IntegerField(required=False, widget=forms.HiddenInput())
visibility = forms.TypedChoiceField(
label=_("可见性"),
label=_("Visibility"),
initial=0,
coerce=int,
choices=VisibilityType.choices,
@ -31,25 +31,25 @@ class ReviewForm(forms.ModelForm):
COLLABORATIVE_CHOICES = [
(0, _("仅限创建者")),
(1, _("创建者及其互关用户")),
(0, _("creator only")),
(1, _("creator and their mutuals")),
]
class CollectionForm(forms.ModelForm):
# id = forms.IntegerField(required=False, widget=forms.HiddenInput())
title = forms.CharField(label=_("标题"))
brief = MarkdownxFormField(label=_("介绍 (Markdown)"), strip=False)
title = forms.CharField(label=_("Title"))
brief = MarkdownxFormField(label=_("Content (Markdown)"), strip=False)
# share_to_mastodon = forms.BooleanField(label=_("分享到联邦宇宙"), initial=True, required=False)
visibility = forms.TypedChoiceField(
label=_("可见性"),
label=_("Visibility"),
initial=0,
coerce=int,
choices=VisibilityType.choices,
widget=forms.RadioSelect,
)
collaborative = forms.TypedChoiceField(
label=_("协作整理权限"),
label=_("Collaborative editing"),
initial=0,
coerce=int,
choices=COLLABORATIVE_CHOICES,