Implement basic Music functions
This commit is contained in:
parent
bb311a2a92
commit
e5bc7f55c0
49 changed files with 4957 additions and 159 deletions
|
@ -25,6 +25,7 @@ urlpatterns = [
|
|||
path('users/', include('users.urls')),
|
||||
path('books/', include('books.urls')),
|
||||
path('movies/', include('movies.urls')),
|
||||
path('music/', include('music.urls')),
|
||||
path('announcement/', include('management.urls')),
|
||||
path('', include('common.urls')),
|
||||
|
||||
|
|
|
@ -77,42 +77,16 @@ class BookForm(forms.ModelForm):
|
|||
return isbn
|
||||
|
||||
|
||||
class BookMarkForm(forms.ModelForm):
|
||||
IS_PRIVATE_CHOICES = [
|
||||
(True, _("仅关注者")),
|
||||
(False, _("公开")),
|
||||
]
|
||||
STATUS_CHOICES = [(v, BookMarkStatusTranslator(v)) for v in MarkStatusEnum.values]
|
||||
class BookMarkForm(MarkForm):
|
||||
|
||||
STATUS_CHOICES = [(v, BookMarkStatusTranslator(v))
|
||||
for v in MarkStatusEnum.values]
|
||||
|
||||
id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
share_to_mastodon = forms.BooleanField(label=_("分享到长毛象"), initial=True, required=False)
|
||||
rating = forms.IntegerField(validators=[RatingValidator()], widget=forms.HiddenInput(), required=False)
|
||||
status = forms.ChoiceField(
|
||||
label=_(""),
|
||||
widget=forms.RadioSelect(),
|
||||
choices=STATUS_CHOICES
|
||||
)
|
||||
is_private = RadioBooleanField(
|
||||
label=_("可见性"),
|
||||
initial=True,
|
||||
choices=IS_PRIVATE_CHOICES
|
||||
)
|
||||
tags = TagField(
|
||||
required=False,
|
||||
widget=TagInput(attrs={'placeholder': _("回车增加标签")}),
|
||||
label = _("标签")
|
||||
)
|
||||
text = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.Textarea(
|
||||
attrs={
|
||||
"placeholder": _("最多只能写360字哦~"),
|
||||
"maxlength": 360
|
||||
}
|
||||
),
|
||||
|
||||
label = _("短评"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = BookMark
|
||||
|
@ -132,18 +106,8 @@ class BookMarkForm(forms.ModelForm):
|
|||
}
|
||||
|
||||
|
||||
class BookReviewForm(forms.ModelForm):
|
||||
IS_PRIVATE_CHOICES = [
|
||||
(True, _("仅关注者")),
|
||||
(False, _("公开")),
|
||||
]
|
||||
share_to_mastodon = forms.BooleanField(label=_("分享到长毛象"), initial=True, required=False)
|
||||
id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
is_private = RadioBooleanField(
|
||||
label=_("可见性"),
|
||||
initial=True,
|
||||
choices=IS_PRIVATE_CHOICES
|
||||
)
|
||||
class BookReviewForm(ReviewForm):
|
||||
|
||||
class Meta:
|
||||
model = BookReview
|
||||
fields = [
|
||||
|
|
|
@ -47,7 +47,7 @@ class Book(Entity):
|
|||
# since data origin is not formatted and might be CNY USD or other currency, use char instead
|
||||
price = models.CharField(_("pricing"), blank=True, default='', max_length=50)
|
||||
pages = models.PositiveIntegerField(_("pages"), null=True, blank=True)
|
||||
isbn = models.CharField(_("ISBN"), blank=True, null=True, max_length=20, db_index=True)
|
||||
isbn = models.CharField(_("ISBN"), blank=True, null=False, max_length=20, db_index=True, default='')
|
||||
# to store previously scrapped data
|
||||
cover = models.ImageField(_("cover picture"), upload_to=book_cover_path, default=DEFAULT_BOOK_IMAGE, blank=True)
|
||||
contents = models.TextField(blank=True, default="")
|
||||
|
|
|
@ -52,9 +52,12 @@
|
|||
{% endif %}
|
||||
|
||||
{% if book.last_editor %}
|
||||
<a href="{% url 'users:home' book.last_editor.id %}">
|
||||
<div>{% trans '最近编辑者:' %}{{ book.last_editor | default:"" }}</div>
|
||||
</a>
|
||||
<div>
|
||||
{% trans '最近编辑者:' %}
|
||||
<a href="{% url 'users:home' book.last_editor.id %}">
|
||||
<span>{{ book.last_editor | default:"" }}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div>{% trans '上次编辑时间:' %}{{ book.edited_time }}</div>
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
<div>
|
||||
<a href="{% url 'books:update' book.id %}">{% trans '编辑这本书' %}</a>
|
||||
{% if user.is_staff %}
|
||||
<a href="{% url 'books:delete' book.id %}"> / {% trans '删除' %}</a>
|
||||
/<a href="{% url 'books:delete' book.id %}"> {% trans '删除' %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -174,3 +174,55 @@ class HstoreField(forms.CharField):
|
|||
if len(pairs) == 1:
|
||||
pairs = (pairs,)
|
||||
return pairs
|
||||
|
||||
|
||||
#############################
|
||||
# Form
|
||||
#############################
|
||||
|
||||
class MarkForm(forms.ModelForm):
|
||||
IS_PRIVATE_CHOICES = [
|
||||
(True, _("仅关注者")),
|
||||
(False, _("公开")),
|
||||
]
|
||||
|
||||
id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
share_to_mastodon = forms.BooleanField(
|
||||
label=_("分享到长毛象"), initial=True, required=False)
|
||||
rating = forms.IntegerField(
|
||||
validators=[RatingValidator()], widget=forms.HiddenInput(), required=False)
|
||||
is_private = RadioBooleanField(
|
||||
label=_("可见性"),
|
||||
initial=True,
|
||||
choices=IS_PRIVATE_CHOICES
|
||||
)
|
||||
tags = TagField(
|
||||
required=False,
|
||||
widget=TagInput(attrs={'placeholder': _("回车增加标签")}),
|
||||
label=_("标签")
|
||||
)
|
||||
text = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.Textarea(
|
||||
attrs={
|
||||
"placeholder": _("最多只能写360字哦~"),
|
||||
"maxlength": 360
|
||||
}
|
||||
),
|
||||
|
||||
label=_("短评"),
|
||||
)
|
||||
|
||||
class ReviewForm(forms.ModelForm):
|
||||
IS_PRIVATE_CHOICES = [
|
||||
(True, _("仅关注者")),
|
||||
(False, _("公开")),
|
||||
]
|
||||
share_to_mastodon = forms.BooleanField(
|
||||
label=_("分享到长毛象"), initial=True, required=False)
|
||||
id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
is_private = RadioBooleanField(
|
||||
label=_("可见性"),
|
||||
initial=True,
|
||||
choices=IS_PRIVATE_CHOICES
|
||||
)
|
||||
|
|
|
@ -32,8 +32,8 @@ class Entity(models.Model):
|
|||
edited_time = models.DateTimeField(auto_now_add=True)
|
||||
last_editor = models.ForeignKey(
|
||||
User, on_delete=models.SET_NULL, related_name='%(class)s_last_editor', null=True, blank=False)
|
||||
brief = models.TextField(blank=True, default="")
|
||||
other_info = postgres.JSONField(
|
||||
brief = models.TextField(_("简介"), blank=True, default="")
|
||||
other_info = postgres.JSONField(_("其他信息"),
|
||||
blank=True, null=True, encoder=DjangoJSONEncoder, default=dict)
|
||||
# source_url should include shceme, which is normally https://
|
||||
source_url = models.URLField(_("URL"), max_length=500, unique=True)
|
||||
|
@ -114,7 +114,7 @@ class Entity(models.Model):
|
|||
"""
|
||||
raise NotImplementedError("Subclass should implement this method.")
|
||||
|
||||
def get_revies_manager(self):
|
||||
def get_reviews_manager(self):
|
||||
"""
|
||||
Normally this won't be used.
|
||||
There is no ocassion where visitor can simply view all the reviews.
|
||||
|
|
|
@ -4,6 +4,7 @@ import random
|
|||
import logging
|
||||
from lxml import html
|
||||
import re
|
||||
import dateparser
|
||||
from boofilsic.settings import LUMINATI_USERNAME, LUMINATI_PASSWORD, DEBUG
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from common.models import SourceSiteEnum
|
||||
|
@ -11,6 +12,8 @@ from movies.models import Movie, MovieGenreEnum
|
|||
from movies.forms import MovieForm
|
||||
from books.models import Book
|
||||
from books.forms import BookForm
|
||||
from music.models import Album, Song
|
||||
from music.forms import AlbumForm, SongForm
|
||||
|
||||
|
||||
RE_NUMBERS = re.compile(r"\d+\d*")
|
||||
|
@ -18,7 +21,7 @@ RE_WHITESPACES = re.compile(r"\s+")
|
|||
|
||||
|
||||
DEFAULT_REQUEST_HEADERS = {
|
||||
'Host': 'book.douban.com',
|
||||
'Host': '',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:70.0) Gecko/20100101 Firefox/70.0',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
|
||||
|
@ -65,7 +68,7 @@ class AbstractScraper:
|
|||
|
||||
# subclasses must specify those two variables
|
||||
# site means general sites, like amazon/douban etc
|
||||
site = None
|
||||
site_name = None
|
||||
# host means technically hostname
|
||||
host = None
|
||||
# corresponding data class
|
||||
|
@ -78,18 +81,26 @@ class AbstractScraper:
|
|||
def __init_subclass__(cls, **kwargs):
|
||||
# this statement initialize the subclasses
|
||||
super().__init_subclass__(**kwargs)
|
||||
assert cls.site is not None, "class variable `site` must be specified"
|
||||
assert cls.host is not None, "class variable `host` must be specified"
|
||||
assert cls.site_name is not None, "class variable `site_name` must be specified"
|
||||
assert bool(cls.host), "class variable `host` must be specified"
|
||||
assert cls.data_class is not None, "class variable `data_class` must be specified"
|
||||
assert cls.form_class is not None, "class variable `form_class` must be specified"
|
||||
assert cls.regex is not None, "class variable `regex` must be specified"
|
||||
assert isinstance(cls.host, str), "`host` must be type str"
|
||||
assert cls.site in SourceSiteEnum, "`site` must be one of `SourceSiteEnum` value"
|
||||
assert hasattr(cls, 'scrape') and callable(cls.scrape), "scaper must have method `.scrape()`"
|
||||
assert isinstance(cls.host, str) or (isinstance(cls.host, list) and isinstance(
|
||||
cls.host[0], str)), "`host` must be type str or list"
|
||||
assert cls.site_name in SourceSiteEnum, "`site_name` must be one of `SourceSiteEnum` value"
|
||||
assert hasattr(cls, 'scrape') and callable(
|
||||
cls.scrape), "scaper must have method `.scrape()`"
|
||||
|
||||
# decorate the scrape method
|
||||
cls.scrape = classmethod(log_url(cls.scrape))
|
||||
scraper_registry[cls.host] = cls
|
||||
|
||||
|
||||
# register scraper
|
||||
if isinstance(cls.host, list):
|
||||
for host in cls.host:
|
||||
scraper_registry[host] = cls
|
||||
else:
|
||||
scraper_registry[cls.host] = cls
|
||||
|
||||
def scrape(self, url):
|
||||
"""
|
||||
|
@ -99,7 +110,6 @@ class AbstractScraper:
|
|||
"""
|
||||
raise NotImplementedError("Subclass should implement this method")
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_effective_url(cls, raw_url):
|
||||
url = cls.regex.findall(raw_url)
|
||||
|
@ -113,14 +123,15 @@ class AbstractScraper:
|
|||
|
||||
session_id = random.random()
|
||||
proxy_url = ('http://%s-country-cn-session-%s:%s@zproxy.lum-superproxy.io:%d' %
|
||||
(LUMINATI_USERNAME, session_id, LUMINATI_PASSWORD, PORT))
|
||||
(LUMINATI_USERNAME, session_id, LUMINATI_PASSWORD, PORT))
|
||||
proxies = {
|
||||
'http': proxy_url,
|
||||
'https': proxy_url,
|
||||
}
|
||||
# if DEBUG:
|
||||
# proxies = None
|
||||
r = requests.get(url, proxies=proxies, headers=headers, timeout=TIMEOUT)
|
||||
r = requests.get(url, proxies=proxies,
|
||||
headers=headers, timeout=TIMEOUT)
|
||||
# r = requests.get(url, headers=DEFAULT_REQUEST_HEADERS, timeout=TIMEOUT)
|
||||
|
||||
return html.fromstring(r.content.decode('utf-8'))
|
||||
|
@ -132,7 +143,7 @@ class AbstractScraper:
|
|||
raw_img = None
|
||||
session_id = random.random()
|
||||
proxy_url = ('http://%s-country-cn-session-%s:%s@zproxy.lum-superproxy.io:%d' %
|
||||
(LUMINATI_USERNAME, session_id, LUMINATI_PASSWORD, PORT))
|
||||
(LUMINATI_USERNAME, session_id, LUMINATI_PASSWORD, PORT))
|
||||
proxies = {
|
||||
'http': proxy_url,
|
||||
'https': proxy_url,
|
||||
|
@ -159,7 +170,7 @@ class AbstractScraper:
|
|||
|
||||
|
||||
class DoubanBookScraper(AbstractScraper):
|
||||
site = SourceSiteEnum.DOUBAN.value
|
||||
site_name = SourceSiteEnum.DOUBAN.value
|
||||
host = "book.douban.com"
|
||||
data_class = Book
|
||||
form_class = BookForm
|
||||
|
@ -234,7 +245,8 @@ class DoubanBookScraper(AbstractScraper):
|
|||
|
||||
brief_elem = content.xpath(
|
||||
"//h2/span[text()='内容简介']/../following-sibling::div[1]//div[@class='intro'][not(ancestor::span[@class='short'])]/p/text()")
|
||||
brief = '\n'.join(p.strip() for p in brief_elem) if brief_elem else None
|
||||
brief = '\n'.join(p.strip()
|
||||
for p in brief_elem) if brief_elem else None
|
||||
|
||||
contents = None
|
||||
try:
|
||||
|
@ -311,14 +323,14 @@ class DoubanBookScraper(AbstractScraper):
|
|||
'brief': brief,
|
||||
'contents': contents,
|
||||
'other_info': other,
|
||||
'source_site': self.site,
|
||||
'source_site': self.site_name,
|
||||
'source_url': self.get_effective_url(url),
|
||||
}
|
||||
return data, raw_img
|
||||
|
||||
|
||||
class DoubanMovieScraper(AbstractScraper):
|
||||
site = SourceSiteEnum.DOUBAN.value
|
||||
site_name = SourceSiteEnum.DOUBAN.value
|
||||
host = 'movie.douban.com'
|
||||
data_class = Movie
|
||||
form_class = MovieForm
|
||||
|
@ -327,7 +339,7 @@ class DoubanMovieScraper(AbstractScraper):
|
|||
|
||||
def scrape(self, url):
|
||||
headers = DEFAULT_REQUEST_HEADERS.copy()
|
||||
headers['Host'] = 'movie.douban.com'
|
||||
headers['Host'] = self.host
|
||||
content = self.download_page(url, headers)
|
||||
|
||||
# parsing starts here
|
||||
|
@ -483,8 +495,105 @@ class DoubanMovieScraper(AbstractScraper):
|
|||
'single_episode_length': single_episode_length,
|
||||
'brief': brief,
|
||||
'is_series': is_series,
|
||||
'source_site': self.site,
|
||||
'source_site': self.site_name,
|
||||
'source_url': self.get_effective_url(url),
|
||||
}
|
||||
return data, raw_img
|
||||
|
||||
|
||||
class DoubanAlbumScraper(AbstractScraper):
|
||||
site_name = SourceSiteEnum.DOUBAN.value
|
||||
host = 'music.douban.com'
|
||||
data_class = Album
|
||||
form_class = AlbumForm
|
||||
|
||||
regex = re.compile(r"https://music.douban.com/subject/\d+/{0,1}")
|
||||
|
||||
def scrape(self, url):
|
||||
headers = DEFAULT_REQUEST_HEADERS.copy()
|
||||
headers['Host'] = self.host
|
||||
content = self.download_page(url, headers)
|
||||
|
||||
# parsing starts here
|
||||
try:
|
||||
title = content.xpath("//h1/span/text()")[0].strip()
|
||||
except IndexError:
|
||||
raise ValueError("given url contains no album info")
|
||||
if not title:
|
||||
raise ValueError("given url contains no album info")
|
||||
|
||||
|
||||
artists_elem = content.xpath("""//div[@id='info']/span/span[@class='pl']/a/text()""")
|
||||
artist = None if not artists_elem else artists_elem
|
||||
|
||||
genre_elem = content.xpath(
|
||||
"//div[@id='info']//span[text()='流派:']/following::text()[1]")
|
||||
genre = genre_elem[0].strip() if genre_elem else None
|
||||
|
||||
date_elem = content.xpath(
|
||||
"//div[@id='info']//span[text()='发行时间:']/following::text()[1]")
|
||||
release_date = dateparser.parse(date_elem[0].strip(), settings={
|
||||
'PREFER_DAY_OF_MONTH': 'first'}) if date_elem else None
|
||||
|
||||
company_elem = content.xpath(
|
||||
"//div[@id='info']//span[text()='出版者:']/following::text()[1]")
|
||||
company = company_elem[0].strip() if company_elem else None
|
||||
|
||||
track_list_elem = content.xpath(
|
||||
"//div[@class='track-list']/div[@class='indent']/div/text()"
|
||||
)
|
||||
if track_list_elem:
|
||||
track_list = '\n'.join([track.strip() for track in track_list_elem])
|
||||
else:
|
||||
track_list = None
|
||||
|
||||
brief_elem = content.xpath("//span[@class='all hidden']")
|
||||
if not brief_elem:
|
||||
brief_elem = content.xpath("//span[@property='v:summary']")
|
||||
brief = '\n'.join([e.strip() for e in brief_elem[0].xpath(
|
||||
'./text()')]) if brief_elem else None
|
||||
|
||||
other_info = {}
|
||||
other_elem = content.xpath(
|
||||
"//div[@id='info']//span[text()='又名:']/following-sibling::text()[1]")
|
||||
if other_elem:
|
||||
other_info['又名'] = other_elem[0].strip()
|
||||
other_elem = content.xpath(
|
||||
"//div[@id='info']//span[text()='专辑类型:']/following-sibling::text()[1]")
|
||||
if other_elem:
|
||||
other_info['专辑类型'] = other_elem[0].strip()
|
||||
other_elem = content.xpath(
|
||||
"//div[@id='info']//span[text()='介质:']/following-sibling::text()[1]")
|
||||
if other_elem:
|
||||
other_info['介质'] = other_elem[0].strip()
|
||||
other_elem = content.xpath(
|
||||
"//div[@id='info']//span[text()='ISRC:']/following-sibling::text()[1]")
|
||||
if other_elem:
|
||||
other_info['ISRC'] = other_elem[0].strip()
|
||||
other_elem = content.xpath(
|
||||
"//div[@id='info']//span[text()='条形码:']/following-sibling::text()[1]")
|
||||
if other_elem:
|
||||
other_info['条形码'] = other_elem[0].strip()
|
||||
other_elem = content.xpath(
|
||||
"//div[@id='info']//span[text()='碟片数:']/following-sibling::text()[1]")
|
||||
if other_elem:
|
||||
other_info['碟片数'] = other_elem[0].strip()
|
||||
|
||||
img_url_elem = content.xpath("//div[@id='mainpic']//img/@src")
|
||||
img_url = img_url_elem[0].strip() if img_url_elem else None
|
||||
raw_img = self.download_image(img_url)
|
||||
|
||||
data = {
|
||||
'title': title,
|
||||
'artist': artist,
|
||||
'genre': genre,
|
||||
'release_date': release_date,
|
||||
'duration': None,
|
||||
'company': company,
|
||||
'track_list': track_list,
|
||||
'brief': brief,
|
||||
'other_info': other_info,
|
||||
'source_site': self.site_name,
|
||||
'source_url': self.get_effective_url(url),
|
||||
}
|
||||
return data, raw_img
|
||||
|
||||
|
|
|
@ -374,6 +374,9 @@ input[type='search'],
|
|||
input[type='tel'],
|
||||
input[type='text'],
|
||||
input[type='url'],
|
||||
input[type='date'],
|
||||
input[type='time'],
|
||||
input[type='color'],
|
||||
textarea,
|
||||
select {
|
||||
-webkit-appearance: none;
|
||||
|
@ -396,6 +399,9 @@ input[type='search']:focus,
|
|||
input[type='tel']:focus,
|
||||
input[type='text']:focus,
|
||||
input[type='url']:focus,
|
||||
input[type='date']:focus,
|
||||
input[type='time']:focus,
|
||||
input[type='color']:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
border-color: #00a1cc;
|
||||
|
@ -409,6 +415,9 @@ input[type='search']::-webkit-input-placeholder,
|
|||
input[type='tel']::-webkit-input-placeholder,
|
||||
input[type='text']::-webkit-input-placeholder,
|
||||
input[type='url']::-webkit-input-placeholder,
|
||||
input[type='date']::-webkit-input-placeholder,
|
||||
input[type='time']::-webkit-input-placeholder,
|
||||
input[type='color']::-webkit-input-placeholder,
|
||||
textarea::-webkit-input-placeholder,
|
||||
select::-webkit-input-placeholder {
|
||||
color: #ccc;
|
||||
|
@ -421,6 +430,9 @@ input[type='search']:-ms-input-placeholder,
|
|||
input[type='tel']:-ms-input-placeholder,
|
||||
input[type='text']:-ms-input-placeholder,
|
||||
input[type='url']:-ms-input-placeholder,
|
||||
input[type='date']:-ms-input-placeholder,
|
||||
input[type='time']:-ms-input-placeholder,
|
||||
input[type='color']:-ms-input-placeholder,
|
||||
textarea:-ms-input-placeholder,
|
||||
select:-ms-input-placeholder {
|
||||
color: #ccc;
|
||||
|
@ -433,6 +445,9 @@ input[type='search']::-ms-input-placeholder,
|
|||
input[type='tel']::-ms-input-placeholder,
|
||||
input[type='text']::-ms-input-placeholder,
|
||||
input[type='url']::-ms-input-placeholder,
|
||||
input[type='date']::-ms-input-placeholder,
|
||||
input[type='time']::-ms-input-placeholder,
|
||||
input[type='color']::-ms-input-placeholder,
|
||||
textarea::-ms-input-placeholder,
|
||||
select::-ms-input-placeholder {
|
||||
color: #ccc;
|
||||
|
@ -445,6 +460,9 @@ input[type='search']::placeholder,
|
|||
input[type='tel']::placeholder,
|
||||
input[type='text']::placeholder,
|
||||
input[type='url']::placeholder,
|
||||
input[type='date']::placeholder,
|
||||
input[type='time']::placeholder,
|
||||
input[type='color']::placeholder,
|
||||
textarea::placeholder,
|
||||
select::placeholder {
|
||||
color: #ccc;
|
||||
|
@ -1267,7 +1285,9 @@ select::placeholder {
|
|||
}
|
||||
|
||||
.entity-list .entity-list__entity-info--full-length {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.entity-list .entity-list__entity-brief {
|
||||
|
@ -1771,7 +1791,7 @@ select::placeholder {
|
|||
line-height: unset;
|
||||
height: unset;
|
||||
padding: 4px 15px;
|
||||
margin: 0 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.action-panel {
|
||||
|
|
2
common/static/css/boofilsic.min.css
vendored
2
common/static/css/boofilsic.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -8,6 +8,8 @@ function keyValueInput(valueKeyWidget, hiddenInput) {
|
|||
if (placeholderValue == null) {
|
||||
placeholderValue = '';
|
||||
}
|
||||
// assign existing pairs to hidden input
|
||||
setHiddenInput(valueKeyWidget);
|
||||
|
||||
let newInputPair = $('<input type="text"' + 'placeholder=' + placeholderKey + '><input type="text"' + 'placeholder=' + placeholderValue + '>');
|
||||
valueKeyWidget.append(newInputPair.clone());
|
||||
|
@ -27,7 +29,7 @@ function keyValueInput(valueKeyWidget, hiddenInput) {
|
|||
$(this).next().remove();
|
||||
$(this).remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
valueKeyWidget.on('input', ':nth-last-child(3)', function () {
|
||||
if (!$(this).val() && !$(this).prev().val() && valueKeyWidget.children("input").length > 2) {
|
||||
|
@ -37,12 +39,16 @@ function keyValueInput(valueKeyWidget, hiddenInput) {
|
|||
});
|
||||
|
||||
valueKeyWidget.on('input', function () {
|
||||
let keys = $(this).children(":nth-child(odd)").map(function () {
|
||||
setHiddenInput(this);
|
||||
});
|
||||
|
||||
function setHiddenInput(elem) {
|
||||
let keys = $(elem).children(":nth-child(odd)").map(function () {
|
||||
if ($(this).val()) {
|
||||
return $(this).val();
|
||||
}
|
||||
}).get();
|
||||
let values = $(this).children(":nth-child(even)").map(function () {
|
||||
let values = $(elem).children(":nth-child(even)").map(function () {
|
||||
if ($(this).val()) {
|
||||
return $(this).val();
|
||||
}
|
||||
|
@ -55,7 +61,7 @@ function keyValueInput(valueKeyWidget, hiddenInput) {
|
|||
finalValue.push(JSON.stringify(json))
|
||||
});
|
||||
hiddenInput.val(finalValue.toString());
|
||||
} else if(keys.length - values.length == 1) {
|
||||
} else if (keys.length - values.length == 1) {
|
||||
let finalValue = [];
|
||||
keys.forEach(function (key, i) {
|
||||
let json = new Object;
|
||||
|
@ -66,8 +72,10 @@ function keyValueInput(valueKeyWidget, hiddenInput) {
|
|||
}
|
||||
finalValue.push(JSON.stringify(json))
|
||||
});
|
||||
hiddenInput.val(finalValue.toString());
|
||||
hiddenInput.val(finalValue.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ $aside-section-padding-mobile: 24px 25px 10px 25px
|
|||
line-height: unset;
|
||||
height: unset;
|
||||
padding: 4px 15px;
|
||||
margin: 0 5px;
|
||||
margin: 5px;
|
||||
|
||||
.action-panel
|
||||
margin-bottom: 20px
|
||||
|
|
|
@ -68,6 +68,9 @@ input[type='search'],
|
|||
input[type='tel'],
|
||||
input[type='text'],
|
||||
input[type='url'],
|
||||
input[type='date'],
|
||||
input[type='time'],
|
||||
input[type='color'],
|
||||
textarea,
|
||||
select
|
||||
appearance: none // Removes awkward default styles on some inputs for iOS
|
||||
|
|
|
@ -66,7 +66,9 @@ $sub-section-title-margin: 8px
|
|||
position: relative
|
||||
top: 0.52em
|
||||
&--full-length
|
||||
display: block
|
||||
max-width: 100%
|
||||
margin-bottom: 12px
|
||||
|
||||
& &__entity-brief
|
||||
margin-top: 8px
|
||||
|
|
|
@ -175,7 +175,7 @@
|
|||
<div class="entity-list__rating entity-list__rating--empty"> {% trans '暂无评分' %}</div>
|
||||
{% endif %}
|
||||
|
||||
<span class="entity-list__entity-info entity-list__entity-info--full-length">
|
||||
<span class="entity-list__entity-info ">
|
||||
|
||||
|
||||
{% if movie.director %}{% trans '导演' %}
|
||||
|
@ -196,7 +196,7 @@
|
|||
{% for actor in movie.actor %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>{{ actor }}</span>
|
||||
{% if forloop.counter <= 5 %}
|
||||
{% if not forloop.counter == 5 %} / {% endif %}
|
||||
{% if not forloop.counter == 5 and not forloop.last %} / {% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
@ -219,6 +219,90 @@
|
|||
|
||||
</li>
|
||||
{% endwith %}
|
||||
|
||||
{% elif item.category_name|lower == 'album' or item.category_name|lower == 'song' %}
|
||||
|
||||
{% with music=item %}
|
||||
<li class="entity-list__entity">
|
||||
<div class="entity-list__entity-img-wrapper">
|
||||
|
||||
{% comment %}
|
||||
|
||||
<a href="{% url 'music:retrieve' music.id %}">
|
||||
<img src="{{ music.cover.url }}" alt="" class="entity-list__entity-img">
|
||||
</a>
|
||||
{% endcomment %}
|
||||
|
||||
</div>
|
||||
<div class="entity-list__entity-text">
|
||||
<div class="entity-list__entity-title">
|
||||
|
||||
{% comment %}
|
||||
|
||||
{% if item.category_name == 'album' %}
|
||||
<a href="{% url 'music:retrieve_album' music.id %}" class="entity-list__entity-link">
|
||||
{{ music.title | highlight:request.GET.q }}
|
||||
</a>
|
||||
{% elif item.category_name = 'song' %}
|
||||
<a href="{% url 'music:retrieve_song' music.id %}" class="entity-list__entity-link">
|
||||
{{ music.title | highlight:request.GET.q }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endcomment %}
|
||||
|
||||
|
||||
|
||||
{% if not request.GET.c or request.GET.c != 'music' and request.GET.c != 'book' and request.GET.c != 'music' %}
|
||||
<span class="entity-list__entity-category">[{{item.verbose_category_name}}]</span>
|
||||
{% endif %}
|
||||
<a href="{{ music.source_url }}">
|
||||
<span class="source-label source-label__{{ music.source_site }}">{{ music.get_source_site_display }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% if music.rating %}
|
||||
<div class="rating-star entity-list__rating-star" data-rating-score="{{ music.rating | floatformat:"0" }}"></div>
|
||||
<span class="entity-list__rating-score rating-score">{{ music.rating }}</span>
|
||||
{% else %}
|
||||
<div class="entity-list__rating entity-list__rating--empty"> {% trans '暂无评分' %}</div>
|
||||
{% endif %}
|
||||
|
||||
<span class="entity-list__entity-info ">
|
||||
{% if music.genre %}{% trans '流派' %}
|
||||
{{ music.genre }} /
|
||||
{% endif %}
|
||||
|
||||
{% if music.release_date %} {% trans '发行日期' %}
|
||||
{{ music.release_date }}
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="entity-list__entity-info entity-list__entity-info--full-length">
|
||||
{% if music.artist %}{% trans '艺术家' %}
|
||||
{% for artist in music.artist %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>{{ artist }}</span>
|
||||
{% if forloop.counter <= 5 %} {% if not forloop.counter == 5 and not forloop.last %} / {% endif %} {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
<p class="entity-list__entity-brief">
|
||||
{{ music.brief | truncate:170 }}
|
||||
</p>
|
||||
<div class="tag-collection">
|
||||
{% for tag_dict in music.tag_list %}
|
||||
{% for k, v in tag_dict.items %}
|
||||
{% if k == 'content' %}
|
||||
<span class="tag-collection__tag">
|
||||
<a href="{% url 'common:search' %}?tag={{ v }}">{{ v }}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
{% endwith %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
@ -278,6 +362,12 @@
|
|||
<a href="{% url 'movies:create' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加电影/剧集' %}</button>
|
||||
</a>
|
||||
|
||||
{% elif request.GET.c|lower == 'music' %}
|
||||
|
||||
<a href="{% url 'movies:create' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加音乐' %}</button>
|
||||
</a>
|
||||
|
||||
{% else %}
|
||||
<a href="{% url 'books:create' %}">
|
||||
|
@ -286,6 +376,12 @@
|
|||
<a href="{% url 'movies:create' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加电影/剧集' %}</button>
|
||||
</a>
|
||||
<a href="{% url 'music:create_album' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加专辑' %}</button>
|
||||
</a>
|
||||
<a href="{% url 'music:create_song' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加单曲' %}</button>
|
||||
</a>
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
@ -296,6 +392,12 @@
|
|||
<a href="{% url 'movies:create' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加电影/剧集' %}</button>
|
||||
</a>
|
||||
<a href="{% url 'music:create_album' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加专辑' %}</button>
|
||||
</a>
|
||||
<a href="{% url 'music:create_song' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加单曲' %}</button>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="add-entity-entries__entry">
|
||||
|
@ -319,6 +421,15 @@
|
|||
<button class="add-entity-entries__button">{% trans '从表瓣剽取数据' %}</button>
|
||||
</a>
|
||||
|
||||
{% elif request.GET.c|lower == 'music' %}
|
||||
|
||||
<div class="add-entity-entries__label">
|
||||
{% trans '或者(≖ ◡ ≖)✧' %}
|
||||
</div>
|
||||
<a href="{% url 'music:scrape_album' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '从表瓣剽取数据' %}</button>
|
||||
</a>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="add-entity-entries__label">
|
||||
|
@ -330,6 +441,9 @@
|
|||
<a href="{% url 'movies:scrape' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '电影/剧集' %}</button>
|
||||
</a>
|
||||
<a href="{% url 'music:scrape_album' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '专辑' %}</button>
|
||||
</a>
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
@ -344,7 +458,9 @@
|
|||
<a href="{% url 'movies:scrape' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '电影/剧集' %}</button>
|
||||
</a>
|
||||
|
||||
<a href="{% url 'music:scrape_album' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '专辑' %}</button>
|
||||
</a>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
<option value="all" {% if request.GET.c and request.GET.c != 'movie' and request.GET.c != 'book' or not request.GET.c %}selected{% endif %}>{% trans '任意' %}</option>
|
||||
<option value="book" {% if request.GET.c and request.GET.c == 'book' %}selected{% endif %}>{% trans '书籍' %}</option>
|
||||
<option value="movie" {% if request.GET.c and request.GET.c == 'movie' %}selected{% endif %}>{% trans '电影' %}</option>
|
||||
<option value="music" {% if request.GET.c and request.GET.c == 'music' %}selected{% endif %}>{% trans '音乐' %}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="navbar__dropdown-btn">• • •</button>
|
||||
|
|
|
@ -13,6 +13,7 @@ from django.db.models import Q, Count
|
|||
from django.http import HttpResponseBadRequest
|
||||
from books.models import Book
|
||||
from movies.models import Movie
|
||||
from music.models import Album, Song
|
||||
from users.models import Report, User
|
||||
from mastodon.decorators import mastodon_request_included
|
||||
from common.models import MarkStatusEnum
|
||||
|
@ -122,22 +123,25 @@ def search(request):
|
|||
except ValidationError as e:
|
||||
pass
|
||||
|
||||
# category, book/movie/record etc
|
||||
# category, book/movie/music etc
|
||||
category = request.GET.get("c", default='').strip().lower()
|
||||
|
||||
def book_param_handler():
|
||||
q = Q()
|
||||
query_args = []
|
||||
|
||||
# keywords
|
||||
keywords = request.GET.get("q", default='').strip()
|
||||
# tag
|
||||
tag = request.GET.get("tag", default='')
|
||||
|
||||
if not (keywords or tag):
|
||||
return []
|
||||
|
||||
for keyword in [keywords]:
|
||||
q = q | Q(title__icontains=keyword)
|
||||
q = q | Q(subtitle__icontains=keyword)
|
||||
q = q | Q(orig_title__icontains=keyword)
|
||||
|
||||
# tag
|
||||
tag = request.GET.get("tag", default='')
|
||||
if tag:
|
||||
q = q & Q(book_tags__content__iexact=tag)
|
||||
|
||||
|
@ -158,6 +162,8 @@ def search(request):
|
|||
elif tag:
|
||||
# search by single tag
|
||||
book.similarity = 0 if book.rating_number is None else book.rating_number
|
||||
else:
|
||||
book.similarity = 0
|
||||
return book.similarity
|
||||
if len(queryset) > 0:
|
||||
ordered_queryset = sorted(queryset, key=calculate_similarity, reverse=True)
|
||||
|
@ -168,16 +174,19 @@ def search(request):
|
|||
def movie_param_handler():
|
||||
q = Q()
|
||||
query_args = []
|
||||
|
||||
# keywords
|
||||
keywords = request.GET.get("q", default='').strip()
|
||||
# tag
|
||||
tag = request.GET.get("tag", default='')
|
||||
|
||||
if not (keywords or tag):
|
||||
return []
|
||||
|
||||
for keyword in [keywords]:
|
||||
q = q | Q(title__icontains=keyword)
|
||||
q = q | Q(other_title__icontains=keyword)
|
||||
q = q | Q(orig_title__icontains=keyword)
|
||||
|
||||
# tag
|
||||
tag = request.GET.get("tag", default='')
|
||||
if tag:
|
||||
q = q & Q(movie_tags__content__iexact=tag)
|
||||
|
||||
|
@ -197,6 +206,8 @@ def search(request):
|
|||
elif tag:
|
||||
# search by single tag
|
||||
movie.similarity = 0 if movie.rating_number is None else movie.rating_number
|
||||
else:
|
||||
movie.similarity = 0
|
||||
return movie.similarity
|
||||
if len(queryset) > 0:
|
||||
ordered_queryset = sorted(queryset, key=calculate_similarity, reverse=True)
|
||||
|
@ -204,11 +215,53 @@ def search(request):
|
|||
ordered_queryset = list(queryset)
|
||||
return ordered_queryset
|
||||
|
||||
def music_param_handler():
|
||||
q = Q()
|
||||
query_args = []
|
||||
|
||||
# keywords
|
||||
keywords = request.GET.get("q", default='').strip()
|
||||
# tag
|
||||
tag = request.GET.get("tag", default='')
|
||||
|
||||
if not (keywords or tag):
|
||||
return []
|
||||
|
||||
# search albums
|
||||
for keyword in [keywords]:
|
||||
q = q | Q(title__icontains=keyword)
|
||||
if tag:
|
||||
q = q & Q(album_tags__content__iexact=tag)
|
||||
|
||||
query_args.append(q)
|
||||
queryset = Album.objects.filter(*query_args).distinct()
|
||||
|
||||
def calculate_similarity(music):
|
||||
if keywords:
|
||||
# search by name
|
||||
similarity, n = 0, 0
|
||||
for keyword in keywords:
|
||||
similarity += SequenceMatcher(None, keyword, music.title).quick_ratio()
|
||||
n += 1
|
||||
music.similarity = similarity / n
|
||||
elif tag:
|
||||
# search by single tag
|
||||
music.similarity = 0 if music.rating_number is None else music.rating_number
|
||||
else:
|
||||
music.similarity = 0
|
||||
return music.similarity
|
||||
if len(queryset) > 0:
|
||||
ordered_queryset = sorted(queryset, key=calculate_similarity, reverse=True)
|
||||
else:
|
||||
ordered_queryset = list(queryset)
|
||||
return ordered_queryset
|
||||
|
||||
def all_param_handler():
|
||||
book_queryset = book_param_handler()
|
||||
movie_queryset = movie_param_handler()
|
||||
music_queryset = music_param_handler()
|
||||
ordered_queryset = sorted(
|
||||
book_queryset + movie_queryset,
|
||||
book_queryset + movie_queryset + music_queryset,
|
||||
key=operator.attrgetter('similarity'),
|
||||
reverse=True
|
||||
)
|
||||
|
@ -217,6 +270,7 @@ def search(request):
|
|||
param_handler = {
|
||||
'book': book_param_handler,
|
||||
'movie': movie_param_handler,
|
||||
'music': music_param_handler,
|
||||
'all': all_param_handler,
|
||||
'': all_param_handler
|
||||
}
|
||||
|
|
|
@ -16,11 +16,6 @@ def MovieMarkStatusTranslator(status):
|
|||
|
||||
|
||||
class MovieForm(forms.ModelForm):
|
||||
# pub_year = forms.IntegerField(
|
||||
# required=False, max_value=9999, min_value=0, label=_("出版年份"))
|
||||
# pub_month = forms.IntegerField(
|
||||
# required=False, max_value=12, min_value=1, label=_("出版月份"))
|
||||
|
||||
id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
genre = forms.MultipleChoiceField(
|
||||
required=False,
|
||||
|
@ -103,52 +98,18 @@ class MovieForm(forms.ModelForm):
|
|||
'is_series': forms.CheckboxInput(attrs={'style': 'width: auto; position: relative; top: 2px'})
|
||||
}
|
||||
|
||||
# def clean_isbn(self):
|
||||
# isbn = self.cleaned_data.get('isbn')
|
||||
# if isbn:
|
||||
# isbn = isbn.strip()
|
||||
# return isbn
|
||||
|
||||
class MovieMarkForm(MarkForm):
|
||||
|
||||
class MovieMarkForm(forms.ModelForm):
|
||||
IS_PRIVATE_CHOICES = [
|
||||
(True, _("仅关注者")),
|
||||
(False, _("公开")),
|
||||
]
|
||||
STATUS_CHOICES = [(v, MovieMarkStatusTranslator(v))
|
||||
for v in MarkStatusEnum.values]
|
||||
|
||||
id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
share_to_mastodon = forms.BooleanField(
|
||||
label=_("分享到长毛象"), initial=True, required=False)
|
||||
rating = forms.IntegerField(
|
||||
validators=[RatingValidator()], widget=forms.HiddenInput(), required=False)
|
||||
status = forms.ChoiceField(
|
||||
label=_(""),
|
||||
widget=forms.RadioSelect(),
|
||||
choices=STATUS_CHOICES
|
||||
)
|
||||
is_private = RadioBooleanField(
|
||||
label=_("可见性"),
|
||||
initial=True,
|
||||
choices=IS_PRIVATE_CHOICES
|
||||
)
|
||||
tags = TagField(
|
||||
required=False,
|
||||
widget=TagInput(attrs={'placeholder': _("回车增加标签")}),
|
||||
label=_("标签")
|
||||
)
|
||||
text = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.Textarea(
|
||||
attrs={
|
||||
"placeholder": _("最多只能写360字哦~"),
|
||||
"maxlength": 360
|
||||
}
|
||||
),
|
||||
|
||||
label=_("短评"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MovieMark
|
||||
|
@ -168,19 +129,7 @@ class MovieMarkForm(forms.ModelForm):
|
|||
}
|
||||
|
||||
|
||||
class MovieReviewForm(forms.ModelForm):
|
||||
IS_PRIVATE_CHOICES = [
|
||||
(True, _("仅关注者")),
|
||||
(False, _("公开")),
|
||||
]
|
||||
share_to_mastodon = forms.BooleanField(
|
||||
label=_("分享到长毛象"), initial=True, required=False)
|
||||
id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
is_private = RadioBooleanField(
|
||||
label=_("可见性"),
|
||||
initial=True,
|
||||
choices=IS_PRIVATE_CHOICES
|
||||
)
|
||||
class MovieReviewForm(ReviewForm):
|
||||
|
||||
class Meta:
|
||||
model = MovieReview
|
||||
|
|
|
@ -54,6 +54,7 @@ class MovieGenreEnum(models.TextChoices):
|
|||
REALITY_TV = 'Reality-TV', _('真人秀')
|
||||
FAMILY = 'Family', _('家庭')
|
||||
TALK_SHOW = 'Talk-Show', _('脱口秀')
|
||||
OTHER = 'Other', _('其他')
|
||||
|
||||
|
||||
MovieGenreTranslator = ChoicesDictGenerator(MovieGenreEnum)
|
||||
|
@ -76,7 +77,7 @@ class Movie(Entity):
|
|||
default=list,
|
||||
)
|
||||
imdb_code = models.CharField(
|
||||
blank=True, max_length=10, null=True, db_index=True)
|
||||
blank=True, max_length=10, null=False, db_index=True, default='')
|
||||
director = postgres.ArrayField(
|
||||
models.CharField(_("director"), blank=True,
|
||||
default='', max_length=100),
|
||||
|
|
|
@ -59,9 +59,12 @@
|
|||
{% endif %}
|
||||
|
||||
{% if movie.last_editor %}
|
||||
<a href="{% url 'users:home' movie.last_editor.id %}">
|
||||
<div>{% trans '最近编辑者:' %}{{ movie.last_editor | default:"" }}</div>
|
||||
</a>
|
||||
<div>
|
||||
{% trans '最近编辑者:' %}
|
||||
<a href="{% url 'users:home' movie.last_editor.id %}">
|
||||
<span>{{ movie.last_editor | default:"" }}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div>{% trans '上次编辑时间:' %}{{ movie.edited_time }}</div>
|
||||
|
|
|
@ -204,7 +204,7 @@
|
|||
<a href="{% url 'movies:update' movie.id %}">{% trans '编辑这部电影' %}</a>
|
||||
{% endif %}
|
||||
{% if user.is_staff %}
|
||||
<a href="{% url 'movies:delete' movie.id %}"> / {% trans '删除' %}</a>
|
||||
/<a href="{% url 'movies:delete' movie.id %}"> {% trans '删除' %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -240,15 +240,6 @@
|
|||
|
||||
</div>
|
||||
|
||||
{% if movie.contents %}
|
||||
<div class="entity-desc" id="contents">
|
||||
<h5 class="entity-desc__title">{% trans '目录' %}</h5>
|
||||
<p class="entity-desc__content">{{ movie.contents | linebreaksbr }}</p>
|
||||
<div class="entity-desc__unfold-button entity-desc__unfold-button--hidden">
|
||||
<a href="javascript:void(0);">展开全部</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="entity-marks">
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ from common.views import PAGE_LINK_NUMBER, jump_or_scrape
|
|||
from common.models import SourceSiteEnum
|
||||
from .models import *
|
||||
from .forms import *
|
||||
from .forms import MovieMarkStatusTranslator
|
||||
from boofilsic.settings import MASTODON_TAGS
|
||||
|
||||
|
||||
|
@ -225,7 +224,7 @@ def retrieve(request, id):
|
|||
}
|
||||
)
|
||||
else:
|
||||
logger.warning('non-GET method at /movie/<id>')
|
||||
logger.warning('non-GET method at /movies/<id>')
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
|
|
0
music/__init__.py
Normal file
0
music/__init__.py
Normal file
11
music/admin.py
Normal file
11
music/admin.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
from django.contrib import admin
|
||||
from .models import *
|
||||
|
||||
admin.site.register(Song)
|
||||
admin.site.register(SongMark)
|
||||
admin.site.register(SongReview)
|
||||
admin.site.register(SongTag)
|
||||
admin.site.register(Album)
|
||||
admin.site.register(AlbumMark)
|
||||
admin.site.register(AlbumReview)
|
||||
admin.site.register(AlbumTag)
|
5
music/apps.py
Normal file
5
music/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MusicConfig(AppConfig):
|
||||
name = 'music'
|
178
music/forms.py
Normal file
178
music/forms.py
Normal file
|
@ -0,0 +1,178 @@
|
|||
from django import forms
|
||||
from django.contrib.postgres.forms import SimpleArrayField
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from .models import *
|
||||
from common.models import MarkStatusEnum
|
||||
from common.forms import *
|
||||
|
||||
|
||||
def MusicMarkStatusTranslator(status):
|
||||
trans_dict = {
|
||||
MarkStatusEnum.DO.value: _("在听"),
|
||||
MarkStatusEnum.WISH.value: _("想听"),
|
||||
MarkStatusEnum.COLLECT.value: _("听过")
|
||||
}
|
||||
return trans_dict[status]
|
||||
|
||||
|
||||
class SongForm(forms.ModelForm):
|
||||
|
||||
id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
other_info = JSONField(required=False, label=_("其他信息"))
|
||||
|
||||
class Meta:
|
||||
model = Song
|
||||
# fields = '__all__'
|
||||
fields = [
|
||||
'id',
|
||||
'title',
|
||||
'source_site',
|
||||
'source_url',
|
||||
'artist',
|
||||
'release_date',
|
||||
'duration',
|
||||
'isrc',
|
||||
'genre',
|
||||
'cover',
|
||||
'album',
|
||||
'brief',
|
||||
'other_info',
|
||||
]
|
||||
widgets = {
|
||||
'artist': forms.TextInput(attrs={'placeholder': _("多个艺术家使用英文逗号分隔")}),
|
||||
'duration': forms.TextInput(attrs={'placeholder': _("毫秒")}),
|
||||
'cover': PreviewImageInput(),
|
||||
}
|
||||
|
||||
|
||||
class SongMarkForm(MarkForm):
|
||||
|
||||
STATUS_CHOICES = [(v, MusicMarkStatusTranslator(v))
|
||||
for v in MarkStatusEnum.values]
|
||||
|
||||
status = forms.ChoiceField(
|
||||
label=_(""),
|
||||
widget=forms.RadioSelect(),
|
||||
choices=STATUS_CHOICES
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = SongMark
|
||||
fields = [
|
||||
'id',
|
||||
'song',
|
||||
'status',
|
||||
'rating',
|
||||
'text',
|
||||
'is_private',
|
||||
]
|
||||
labels = {
|
||||
'rating': _("评分"),
|
||||
}
|
||||
widgets = {
|
||||
'song': forms.TextInput(attrs={"hidden": ""}),
|
||||
}
|
||||
|
||||
|
||||
class SongReviewForm(ReviewForm):
|
||||
|
||||
class Meta:
|
||||
model = SongReview
|
||||
fields = [
|
||||
'id',
|
||||
'song',
|
||||
'title',
|
||||
'content',
|
||||
'is_private'
|
||||
]
|
||||
labels = {
|
||||
'song': "",
|
||||
'title': _("标题"),
|
||||
'content': _("正文"),
|
||||
'share_to_mastodon': _("分享到长毛象")
|
||||
}
|
||||
widgets = {
|
||||
'song': forms.TextInput(attrs={"hidden": ""}),
|
||||
}
|
||||
|
||||
|
||||
class AlbumForm(forms.ModelForm):
|
||||
|
||||
id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
other_info = JSONField(required=False, label=_("其他信息"))
|
||||
|
||||
class Meta:
|
||||
model = Album
|
||||
# fields = '__all__'
|
||||
fields = [
|
||||
'id',
|
||||
'title',
|
||||
'source_site',
|
||||
'source_url',
|
||||
'artist',
|
||||
'company',
|
||||
'release_date',
|
||||
'duration',
|
||||
'genre',
|
||||
'cover',
|
||||
'brief',
|
||||
'track_list',
|
||||
'other_info',
|
||||
]
|
||||
widgets = {
|
||||
'artist': forms.TextInput(attrs={'placeholder': _("多个艺术家使用英文逗号分隔")}),
|
||||
'company': forms.TextInput(attrs={'placeholder': _("多个发行方使用英文逗号分隔")}),
|
||||
'duration': forms.TextInput(attrs={'placeholder': _("毫秒")}),
|
||||
'cover': PreviewImageInput(),
|
||||
}
|
||||
|
||||
|
||||
class AlbumMarkForm(MarkForm):
|
||||
|
||||
STATUS_CHOICES = [(v, MusicMarkStatusTranslator(v))
|
||||
for v in MarkStatusEnum.values]
|
||||
|
||||
status = forms.ChoiceField(
|
||||
label=_(""),
|
||||
widget=forms.RadioSelect(),
|
||||
choices=STATUS_CHOICES
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = AlbumMark
|
||||
fields = [
|
||||
'id',
|
||||
'album',
|
||||
'status',
|
||||
'rating',
|
||||
'text',
|
||||
'is_private',
|
||||
]
|
||||
labels = {
|
||||
'rating': _("评分"),
|
||||
}
|
||||
widgets = {
|
||||
'album': forms.TextInput(attrs={"hidden": ""}),
|
||||
}
|
||||
|
||||
|
||||
class AlbumReviewForm(ReviewForm):
|
||||
|
||||
class Meta:
|
||||
model = AlbumReview
|
||||
fields = [
|
||||
'id',
|
||||
'album',
|
||||
'title',
|
||||
'content',
|
||||
'is_private'
|
||||
]
|
||||
labels = {
|
||||
'album': "",
|
||||
'title': _("标题"),
|
||||
'content': _("正文"),
|
||||
'share_to_mastodon': _("分享到长毛象")
|
||||
}
|
||||
widgets = {
|
||||
'album': forms.TextInput(attrs={"hidden": ""}),
|
||||
}
|
181
music/models.py
Normal file
181
music/models.py
Normal file
|
@ -0,0 +1,181 @@
|
|||
import uuid
|
||||
import django.contrib.postgres.fields as postgres
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db import models
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.shortcuts import reverse
|
||||
from common.models import Entity, Mark, Review, Tag
|
||||
from common.utils import ChoicesDictGenerator
|
||||
from boofilsic.settings import SONG_MEDIA_PATH_ROOT, DEFAULT_SONG_IMAGE, ALBUM_MEDIA_PATH_ROOT, DEFAULT_ALBUM_IMAGE
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
def song_cover_path(instance, filename):
|
||||
ext = filename.split('.')[-1]
|
||||
filename = "%s.%s" % (uuid.uuid4(), ext)
|
||||
root = ''
|
||||
if SONG_MEDIA_PATH_ROOT.endswith('/'):
|
||||
root = SONG_MEDIA_PATH_ROOT
|
||||
else:
|
||||
root = SONG_MEDIA_PATH_ROOT + '/'
|
||||
return root + timezone.now().strftime('%Y/%m/%d') + f'{filename}'
|
||||
|
||||
|
||||
def album_cover_path(instance, filename):
|
||||
ext = filename.split('.')[-1]
|
||||
filename = "%s.%s" % (uuid.uuid4(), ext)
|
||||
root = ''
|
||||
if ALBUM_MEDIA_PATH_ROOT.endswith('/'):
|
||||
root = ALBUM_MEDIA_PATH_ROOT
|
||||
else:
|
||||
root = ALBUM_MEDIA_PATH_ROOT + '/'
|
||||
return root + timezone.now().strftime('%Y/%m/%d') + f'{filename}'
|
||||
|
||||
|
||||
class Album(Entity):
|
||||
title = models.CharField(_("标题"), max_length=500)
|
||||
release_date = models.DateField(
|
||||
_('发行日期'), auto_now=False, auto_now_add=False, null=True, blank=True)
|
||||
cover = models.ImageField(
|
||||
_("封面"), upload_to=album_cover_path, default=DEFAULT_ALBUM_IMAGE, blank=True)
|
||||
duration = models.PositiveIntegerField(_("时长"), null=True, blank=True)
|
||||
artist = postgres.ArrayField(
|
||||
models.CharField(_("artist"), blank=True,
|
||||
default='', max_length=100),
|
||||
null=True,
|
||||
blank=True,
|
||||
default=list,
|
||||
verbose_name=_("艺术家")
|
||||
)
|
||||
genre = models.CharField(_("流派"), blank=True,
|
||||
default='', max_length=100)
|
||||
company = postgres.ArrayField(
|
||||
models.CharField(blank=True,
|
||||
default='', max_length=100),
|
||||
null=True,
|
||||
blank=True,
|
||||
default=list,
|
||||
verbose_name=_("发行方")
|
||||
)
|
||||
track_list = models.TextField(_("曲目"), blank=True, default="")
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("music:retrieve_album", args=[self.id])
|
||||
|
||||
def get_tags_manager(self):
|
||||
return self.album_tags
|
||||
|
||||
@property
|
||||
def verbose_category_name(self):
|
||||
return _("专辑")
|
||||
|
||||
|
||||
class Song(Entity):
|
||||
'''
|
||||
Song(track) entity, can point to entity Album
|
||||
'''
|
||||
title = models.CharField(_("标题"), max_length=500)
|
||||
release_date = models.DateField(_('发行日期'), auto_now=False, auto_now_add=False, null=True, blank=True)
|
||||
isrc = models.CharField(_("ISRC"),
|
||||
blank=True, max_length=15, db_index=True, default='')
|
||||
# duration in ms
|
||||
duration = models.PositiveIntegerField(_("时长"), null=True, blank=True)
|
||||
cover = models.ImageField(
|
||||
_("封面"), upload_to=song_cover_path, default=DEFAULT_SONG_IMAGE, blank=True)
|
||||
artist = postgres.ArrayField(
|
||||
models.CharField(blank=True,
|
||||
default='', max_length=100),
|
||||
null=True,
|
||||
blank=True,
|
||||
default=list,
|
||||
verbose_name=_("艺术家")
|
||||
)
|
||||
genre = models.CharField(_("流派"), blank=True, default='', max_length=100)
|
||||
|
||||
album = models.ForeignKey(
|
||||
Album, models.CASCADE, "album_songs", null=True, blank=True, verbose_name=_("所属专辑"))
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("music:retrieve_song", args=[self.id])
|
||||
|
||||
def get_tags_manager(self):
|
||||
return self.song_tags
|
||||
|
||||
@property
|
||||
def verbose_category_name(self):
|
||||
return _("单曲")
|
||||
|
||||
class SongMark(Mark):
|
||||
song = models.ForeignKey(
|
||||
Song, on_delete=models.CASCADE, related_name='song_marks', null=True)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=['owner', 'song'], name='unique_song_mark')
|
||||
]
|
||||
|
||||
|
||||
class SongReview(Review):
|
||||
song = models.ForeignKey(
|
||||
Song, on_delete=models.CASCADE, related_name='song_reviews', null=True)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=['owner', 'song'], name='unique_song_review')
|
||||
]
|
||||
|
||||
|
||||
class SongTag(Tag):
|
||||
song = models.ForeignKey(
|
||||
Song, on_delete=models.CASCADE, related_name='song_tags', null=True)
|
||||
mark = models.ForeignKey(
|
||||
SongMark, on_delete=models.CASCADE, related_name='songmark_tags', null=True)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=['content', 'mark'], name="unique_songmark_tag")
|
||||
]
|
||||
|
||||
|
||||
class AlbumMark(Mark):
|
||||
album = models.ForeignKey(
|
||||
Album, on_delete=models.CASCADE, related_name='album_marks', null=True)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=['owner', 'album'], name='unique_album_mark')
|
||||
]
|
||||
|
||||
|
||||
class AlbumReview(Review):
|
||||
album = models.ForeignKey(
|
||||
Album, on_delete=models.CASCADE, related_name='album_reviews', null=True)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=['owner', 'album'], name='unique_album_review')
|
||||
]
|
||||
|
||||
|
||||
class AlbumTag(Tag):
|
||||
album = models.ForeignKey(
|
||||
Album, on_delete=models.CASCADE, related_name='album_tags', null=True)
|
||||
mark = models.ForeignKey(
|
||||
AlbumMark, on_delete=models.CASCADE, related_name='albummark_tags', null=True)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=['content', 'mark'], name="unique_albummark_tag")
|
||||
]
|
435
music/templates/music/album_detail.html
Normal file
435
music/templates/music/album_detail.html
Normal file
|
@ -0,0 +1,435 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load strip_scheme %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:title" content="NiceDB音乐 - {{ album.title }}">
|
||||
<meta property="og:type" content="music.album">
|
||||
<meta property="og:url" content="{{ request.build_absolute_uri }}">
|
||||
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{{ album.cover.url }}">
|
||||
<meta property="og:site_name" content="NiceDB">
|
||||
<meta property="og:description"content="{{ album.brief }}">
|
||||
|
||||
<title>{% trans 'NiceDB - 音乐详情' %} | {{ album.title }}</title>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/detail.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="entity-detail">
|
||||
|
||||
<img src="{{ album.cover.url }}" class="entity-detail__img" alt="{{ album.title }}">
|
||||
|
||||
<div class="entity-detail__info">
|
||||
<h5 class="entity-detail__title">
|
||||
|
||||
{{ album.title }}
|
||||
<a href="{{ album.source_url }}"><span class="source-label source-label__{{ album.source_site }}">{{ album.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
|
||||
<div class="entity-detail__fields">
|
||||
<div class="entity-detail__rating">
|
||||
{% if album.rating %}
|
||||
<span class="entity-detail__rating-star rating-star" data-rating-score="{{ album.rating | floatformat:"0" }}"></span>
|
||||
<span class="entity-detail__rating-score"> {{ album.rating }} </span>
|
||||
{% else %}
|
||||
<span> {% trans '评分:暂无评分' %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if album.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in album.artist %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="artist">{{ artist }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if album.artist|length > 5 %}
|
||||
<a href="javascript:void(0);" id="artistMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#artistMore").click(function (e) {
|
||||
$("span.artist:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}</div>
|
||||
<div>{% if album.company %}{% trans '发行方:' %}
|
||||
{% for company in album.company %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="company">{{ company }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if album.company|length > 5 %}
|
||||
<a href="javascript:void(0);" id="companyMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#companyMore").click(function (e) {
|
||||
$("span.company:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}</div>
|
||||
<div>{% if album.release_date %}
|
||||
{% trans '发行日期:' %}{{ album.release_date }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if album.duration %}
|
||||
{% trans '时长:' %}{{ album.get_duration_display }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if album.genre %}
|
||||
{% trans '流派:' %}{{ album.genre }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="entity-detail__fields">
|
||||
{% if album.other_info %}
|
||||
{% for k, v in album.other_info.items %}
|
||||
<div>
|
||||
{{k}}:{{v}}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if album.last_editor %}
|
||||
<div>{% trans '最近编辑者:' %}<a href="{% url 'users:home' album.last_editor.id %}">{{ album.last_editor | default:"" }}</a></div>
|
||||
{% endif %}
|
||||
|
||||
<div>
|
||||
<a href="{% url 'music:update_album' album.id %}">{% trans '编辑这个作品' %}</a>
|
||||
{% if user.is_staff %}
|
||||
/<a href="{% url 'music:delete_album' album.id %}"> {% trans '删除' %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tag-collection">
|
||||
|
||||
{% for tag_dict in album_tag_list %}
|
||||
{% for k, v in tag_dict.items %}
|
||||
{% if k == 'content' %}
|
||||
<span class="tag-collection__tag">
|
||||
<a href="{% url 'common:search' %}?tag={{ v }}">{{ v }}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dividing-line"></div>
|
||||
<div class="entity-desc" id="description">
|
||||
<h5 class="entity-desc__title">{% trans '简介' %}</h5>
|
||||
{% if album.brief %}
|
||||
|
||||
<p class="entity-desc__content">{{ album.brief | linebreaksbr }}</p>
|
||||
<div class="entity-desc__unfold-button entity-desc__unfold-button--hidden">
|
||||
<a href="javascript:void(0);">展开全部</a>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div>{% trans '暂无简介' %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if album.track_list %}
|
||||
<div class="entity-desc" id="description">
|
||||
<h5 class="entity-desc__title">{% trans '曲目' %}</h5>
|
||||
<p class="entity-desc__content">{{ album.track_list | linebreaksbr }}</p>
|
||||
<div class="entity-desc__unfold-button entity-desc__unfold-button--hidden">
|
||||
<a href="javascript:void(0);">展开全部</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if album.album_songs.count %}
|
||||
<div class="entity-desc" id="description">
|
||||
<h5 class="entity-desc__title">{% trans '关联单曲' %}</h5>
|
||||
<!-- TODO: Limit the maximum -->
|
||||
{% for song in album.album_songs.all %}
|
||||
<div>
|
||||
<a href="{% url 'music:retrieve_song' song.id %}">{{ song }}</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<div class="entity-marks">
|
||||
|
||||
<h5 class="entity-marks__title">{% trans '这部作品的标记' %}</h5>
|
||||
{% if mark_list_more %}
|
||||
<a href="{% url 'music:retrieve_album_mark_list' album.id %}" class="entity-marks__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
{% if mark_list %}
|
||||
<ul class="entity-marks__mark-list">
|
||||
{% for others_mark in mark_list %}
|
||||
<li class="entity-marks__mark">
|
||||
<a href="{% url 'users:home' others_mark.owner.id %}" class="entity-marks__owner-link">{{ others_mark.owner.username }}</a>
|
||||
<span>{{ others_mark.get_status_display }}</span>
|
||||
{% if others_mark.rating %}
|
||||
<span class="entity-marks__rating-star rating-star" data-rating-score="{{ others_mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
{% if others_mark.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-marks__mark-time">{{ others_mark.edited_time }}</span>
|
||||
{% if others_mark.text %}
|
||||
<p class="entity-marks__mark-content">{{ others_mark.text }}</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div>{% trans '暂无标记' %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="entity-reviews">
|
||||
<h5 class="entity-reviews__title">{% trans '这部作品的评论' %}</h5>
|
||||
|
||||
{% if review_list_more %}
|
||||
<a href="{% url 'music:retrieve_album_review_list' album.id %}" class="entity-reviews__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
{% if review_list %}
|
||||
<ul class="entity-reviews__review-list">
|
||||
{% for others_review in review_list %}
|
||||
<li class="entity-reviews__review">
|
||||
<a href="{% url 'users:home' others_review.owner.id %}" class="entity-reviews__owner-link">{{ others_review.owner.username }}</a>
|
||||
{% if others_review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-reviews__review-time">{{ others_review.edited_time }}</span>
|
||||
<span class="entity-reviews__review-title"> <a href="{% url 'music:retrieve_album_review' others_review.id %}">{{ others_review.title }}</a></span>
|
||||
<span>{{ others_review.get_plain_content | truncate:100 }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div>{% trans '暂无评论' %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
|
||||
{% if mark %}
|
||||
<div class="mark-panel">
|
||||
|
||||
<span class="mark-panel__status">{% trans '我' %}{{ mark.get_status_display }}</span>
|
||||
{% if mark.status == status_enum.DO.value or mark.status == status_enum.COLLECT.value%}
|
||||
{% if mark.rating %}
|
||||
<span class="mark-panel__rating-star rating-star" data-rating-score="{{ mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if mark.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
<span class="mark-panel__actions">
|
||||
<a href="" class="edit">{% trans '修改' %}</a>
|
||||
<form action="{% url 'music:delete_album_mark' mark.id %}" method="post">
|
||||
{% csrf_token %}
|
||||
<a href="" class="delete">{% trans '删除' %}</a>
|
||||
</form>
|
||||
</span>
|
||||
<div class="mark-panel__clear"></div>
|
||||
|
||||
<div class="mark-panel__time">{{ mark.edited_time }}</div>
|
||||
|
||||
{% if mark.text %}
|
||||
<p class="mark-panel__text">{{ mark.text }}</p>
|
||||
{% endif %}
|
||||
<div class="tag-collection">
|
||||
|
||||
{% for tag in mark_tags %}
|
||||
<span class="tag-collection__tag">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="action-panel" id="addMarkPanel">
|
||||
<div class="action-panel__label">{% trans '标记这部作品' %}</div>
|
||||
<div class="action-panel__button-group">
|
||||
<button class="action-panel__button" data-status="{{ status_enum.WISH.value }}" id="wishButton">{% trans '想看' %}</button>
|
||||
<button class="action-panel__button" data-status="{{ status_enum.DO.value }}">{% trans '在看' %}</button>
|
||||
<button class="action-panel__button" data-status="{{ status_enum.COLLECT.value }}">{% trans '看过' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="aside-section-wrapper">
|
||||
{% if review %}
|
||||
<div class="review-panel">
|
||||
|
||||
<span class="review-panel__label">{% trans '我的评论' %}</span>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
|
||||
<span class="review-panel__actions">
|
||||
<a href="{% url 'music:update_album_review' review.id %}">{% trans '编辑' %}</a>
|
||||
<a href="{% url 'music:delete_album_review' review.id %}">{% trans '删除' %}</a>
|
||||
</span>
|
||||
|
||||
<div class="review-panel__time">{{ review.edited_time }}</div>
|
||||
|
||||
<a href="{% url 'music:retrieve_album_review' review.id %}" class="review-panel__review-title">
|
||||
{{ review.title }}
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
<div class="action-panel">
|
||||
<div class="action-panel__label">{% trans '我的评论' %}</div>
|
||||
<div class="action-panel__button-group action-panel__button-group--center">
|
||||
|
||||
<a href="{% url 'music:create_album_review' album.id %}">
|
||||
<button class="action-panel__button">{% trans '去写评论' %}</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
<div id="modals">
|
||||
<div class="mark-modal modal">
|
||||
<div class="mark-modal__head">
|
||||
|
||||
{% if not mark %}
|
||||
|
||||
<style>
|
||||
.mark-modal__title::after {
|
||||
content: "{% trans '这部作品' %}";
|
||||
}
|
||||
</style>
|
||||
|
||||
<span class="mark-modal__title"></span>
|
||||
{% else %}
|
||||
<span class="mark-modal__title">{% trans '我的标记' %}</span>
|
||||
{% endif %}
|
||||
|
||||
<span class="mark-modal__close-button modal-close">
|
||||
<span class="icon-cross">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<polygon
|
||||
points="20 2.61 17.39 0 10 7.39 2.61 0 0 2.61 7.39 10 0 17.39 2.61 20 10 12.61 17.39 20 20 17.39 12.61 10 20 2.61">
|
||||
</polygon>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mark-modal__body">
|
||||
<form action="{% url 'music:create_update_album_mark' %}" method="post">
|
||||
{{ mark_form.media }}
|
||||
{% csrf_token %}
|
||||
{{ mark_form.id }}
|
||||
{{ mark_form.album }}
|
||||
{% if mark.rating %}
|
||||
{% endif %}
|
||||
|
||||
<div class="mark-modal__rating-star rating-star-edit"></div>
|
||||
{{ mark_form.rating }}
|
||||
<div id="statusSelection" class="mark-modal__status-radio" {% if not mark %}hidden{% endif %}>
|
||||
{{ mark_form.status }}
|
||||
</div>
|
||||
|
||||
<div class="mark-modal__clear"></div>
|
||||
|
||||
{{ mark_form.text }}
|
||||
|
||||
<div class="mark-modal__tag">
|
||||
<label>{{ mark_form.tags.label }}</label>
|
||||
{{ mark_form.tags }}
|
||||
</div>
|
||||
|
||||
<div class="mark-modal__option">
|
||||
<div class="mark-modal__visibility-radio">
|
||||
<span>{{ mark_form.is_private.label }}:</span>
|
||||
{{ mark_form.is_private }}
|
||||
</div>
|
||||
<div class="mark-modal__share-checkbox">
|
||||
{{ mark_form.share_to_mastodon }}{{ mark_form.share_to_mastodon.label }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mark-modal__confirm-button">
|
||||
<input type="submit" class="button float-right" value="{% trans '提交' %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="confirm-modal modal">
|
||||
<div class="confirm-modal__head">
|
||||
<span class="confirm-modal__title">{% trans '确定要删除你的标记吗?' %}</span>
|
||||
<span class="confirm-modal__close-button modal-close">
|
||||
<span class="icon-cross">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<polygon
|
||||
points="20 2.61 17.39 0 10 7.39 2.61 0 0 2.61 7.39 10 0 17.39 2.61 20 10 12.61 17.39 20 20 17.39 12.61 10 20 2.61">
|
||||
</polygon>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="confirm-modal__body">
|
||||
<div class="confirm-modal__confirm-button">
|
||||
<input type="submit" class="button float-right" value="{% trans '确认' %}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-mask"></div>
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
170
music/templates/music/album_mark_list.html
Normal file
170
music/templates/music/album_mark_list.html
Normal file
|
@ -0,0 +1,170 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load highlight %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ album.title }}{% trans '的标记' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="entity-marks">
|
||||
<h5 class="entity-marks__title entity-marks__title--stand-alone">
|
||||
<a href="{% url 'music:retrieve_album' album.id %}">{{ album.title }}</a>{% trans '的标记' %}
|
||||
</h5>
|
||||
<ul class="entity-marks__mark-list">
|
||||
|
||||
{% for mark in marks %}
|
||||
|
||||
<li class="entity-marks__mark entity-marks__mark--wider">
|
||||
<a href="{% url 'users:home' mark.owner.id %}"
|
||||
class="entity-marks__owner-link">{{ mark.owner.username }}</a>
|
||||
<span>{{ mark.get_status_display }}</span>
|
||||
{% if mark.rating %}
|
||||
<span class="entity-marks__rating-star rating-star"
|
||||
data-rating-score="{{ mark.rating | floatformat:" 0" }}"></span>
|
||||
{% endif %}
|
||||
{% if mark.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z" />
|
||||
</svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-marks__mark-time">{{ mark.edited_time }}</span>
|
||||
{% if mark.text %}
|
||||
<p class="entity-marks__mark-content">{{ mark.text }}</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
{% empty %}
|
||||
<div>
|
||||
{% trans '无结果' %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
|
||||
{% if marks.pagination.has_prev %}
|
||||
<a href="?page=1" class="pagination__nav-link pagination__nav-link">«</a>
|
||||
<a href="?page={{ marks.previous_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">‹</a>
|
||||
{% endif %}
|
||||
|
||||
{% for page in marks.pagination.page_range %}
|
||||
|
||||
{% if page == marks.pagination.current_page %}
|
||||
<a href="?page={{ page }}"
|
||||
class="pagination__page-link pagination__page-link--current">{{ page }}</a>
|
||||
{% else %}
|
||||
<a href="?page={{ page }}" class="pagination__page-link">{{ page }}</a>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% if marks.pagination.has_next %}
|
||||
<a href="?page={{ marks.next_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--left-margin">›</a>
|
||||
<a href="?page={{ marks.pagination.last_page }}"
|
||||
class="pagination__nav-link">»</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
<div class="entity-card">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'music:retrieve_album' album.id %}"><img src="{{ album.cover.url }}"
|
||||
alt="" class="entity-card__img"></a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper">
|
||||
<h5 class="entity-card__title"><a href="{% url 'music:retrieve_album' album.id %}">
|
||||
{{ album.title }}
|
||||
</a>
|
||||
<a href="{{ album.source_url }}"><span
|
||||
class="source-label source-label__{{ album.source_site }}">
|
||||
{{ album.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
|
||||
<div>{% if album.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in album.artist %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="artist">{{ artist }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if album.artist|length > 5 %}
|
||||
<a href="javascript:void(0);" id="artistMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#artistMore").click(function (e) {
|
||||
$("span.artist:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if album.genre %}{% trans '流派:' %}{{ album.genre }}{% endif %}</div>
|
||||
|
||||
<div>{% if album.release_date %}{% trans '发行日期:' %}{{ album.release_date}}{% endif %}</div>
|
||||
{% if album.rating %}
|
||||
{% trans '评分: ' %}<span class="entity-card__rating-star rating-star"
|
||||
data-rating-score="{{ album.rating | floatformat:" 0" }}"></span>
|
||||
<span class="entity-card__rating-score rating-score">{{ album.rating }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
157
music/templates/music/album_review_detail.html
Normal file
157
music/templates/music/album_review_detail.html
Normal file
|
@ -0,0 +1,157 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:title" content="NiceDB乐评 - {{ review.title }}">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:article:author" content="{{ review.owner.username }}">
|
||||
<meta property="og:url" content="{{ request.build_absolute_uri }}">
|
||||
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{% static 'img/logo_square.svg' %}">
|
||||
<title>{% trans 'NiceDB - 评论详情' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="review-head">
|
||||
<h5 class="review-head__title">
|
||||
{{ review.title }}
|
||||
</h5>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><svg
|
||||
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z" />
|
||||
</svg></span>
|
||||
{% endif %}
|
||||
<div class="review-head__body">
|
||||
<div class="review-head__info">
|
||||
|
||||
<a href="{% url 'users:home' review.owner.id %}"
|
||||
class="review-head__owner-link">{{ review.owner.username }}</a>
|
||||
|
||||
{% if mark %}
|
||||
|
||||
{% if mark.rating %}
|
||||
<span class="review-head__rating-star rating-star"
|
||||
data-rating-score="{{ mark.rating | floatformat:" 0" }}"></span>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
<span class="review-head__time">{{ review.edited_time }}</span>
|
||||
|
||||
</div>
|
||||
<div class="review-head__actions">
|
||||
{% if request.user == review.owner %}
|
||||
<a class="review-head__action-link"
|
||||
href="{% url 'music:update_album_review' review.id %}">{% trans '编辑' %}</a>
|
||||
<a class="review-head__action-link"
|
||||
href="{% url 'music:delete_album_review' review.id %}">{% trans '删除' %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="dividing-line"></div> -->
|
||||
<div id="rawContent">
|
||||
{{ form.content }}
|
||||
</div>
|
||||
{{ form.media }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
<div class="entity-card">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'music:retrieve_album' album.id %}"><img src="{{ album.cover.url }}"
|
||||
alt="" class="entity-card__img"></a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper">
|
||||
<h5 class="entity-card__title"><a href="{% url 'music:retrieve_album' album.id %}">
|
||||
{{ album.title }}
|
||||
</a>
|
||||
<a href="{{ album.source_url }}">
|
||||
<span class="source-label source-label__{{ album.source_site }}">
|
||||
{{ album.get_source_site_display }}
|
||||
</span>
|
||||
</a>
|
||||
</h5>
|
||||
|
||||
<div>{% if album.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in album.artist %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="artist">{{ artist }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if album.artist|length > 5 %}
|
||||
<a href="javascript:void(0);" id="artistMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#artistMore").click(function (e) {
|
||||
$("span.artist:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if album.genre %}{% trans '流派:' %}{{ album.genre }}{% endif %}</div>
|
||||
|
||||
<div>{% if album.release_date %}{% trans '发行日期:' %}{{ album.release_date}}{% endif %}</div>
|
||||
{% if album.rating %}
|
||||
{% trans '评分: ' %}<span class="entity-card__rating-star rating-star"
|
||||
data-rating-score="{{ album.rating | floatformat:" 0" }}"></span>
|
||||
<span class="entity-card__rating-score rating-score">{{ album.rating }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
$(".markdownx textarea").hide();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
167
music/templates/music/album_review_list.html
Normal file
167
music/templates/music/album_review_list.html
Normal file
|
@ -0,0 +1,167 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load highlight %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ album.title }}{% trans '的评论' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="entity-reviews">
|
||||
<h5 class="entity-reviews__title entity-reviews__title--stand-alone">
|
||||
<a href="{% url 'music:retrieve_album' album.id %}">{{ album.title }}</a>{% trans '的评论' %}
|
||||
</h5>
|
||||
<ul class="entity-reviews__review-list">
|
||||
|
||||
{% for review in reviews %}
|
||||
|
||||
<li class="entity-reviews__review entity-reviews__review--wider">
|
||||
|
||||
<a href="{% url 'users:home' review.owner.id %}"
|
||||
class="entity-reviews__owner-link">{{ review.owner.username }}</a>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z" />
|
||||
</svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-reviews__review-time">{{ review.edited_time }}</span>
|
||||
|
||||
|
||||
<span href="{% url 'music:retrieve_album_review' review.id %}"
|
||||
class="entity-reviews__review-title"><a
|
||||
href="{% url 'music:retrieve_album_review' review.id %}">{{ review.title
|
||||
}}</a></span>
|
||||
|
||||
</li>
|
||||
{% empty %}
|
||||
<div>{% trans '无结果' %}</div>
|
||||
{% endfor %}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
|
||||
{% if reviews.pagination.has_prev %}
|
||||
<a href="?page=1" class="pagination__nav-link pagination__nav-link">«</a>
|
||||
<a href="?page={{ reviews.previous_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">‹</a>
|
||||
{% endif %}
|
||||
|
||||
{% for page in reviews.pagination.page_range %}
|
||||
|
||||
{% if page == reviews.pagination.current_page %}
|
||||
<a href="?page={{ page }}"
|
||||
class="pagination__page-link pagination__page-link--current">{{ page }}</a>
|
||||
{% else %}
|
||||
<a href="?page={{ page }}" class="pagination__page-link">{{ page }}</a>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% if reviews.pagination.has_next %}
|
||||
<a href="?page={{ reviews.next_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--left-margin">›</a>
|
||||
<a href="?page={{ reviews.pagination.last_page }}"
|
||||
class="pagination__nav-link">»</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
<div class="entity-card">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'music:retrieve_album' album.id %}"><img src="{{ album.cover.url }}"
|
||||
alt="" class="entity-card__img"></a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper">
|
||||
<h5 class="entity-card__title"><a href="{% url 'music:retrieve_album' album.id %}">
|
||||
{{ album.title }}
|
||||
</a>
|
||||
<a href="{{ album.source_url }}"><span
|
||||
class="source-label source-label__{{ album.source_site }}">
|
||||
{{ album.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
|
||||
<div>{% if album.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in album.artist %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="artist">{{ artist }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if album.artist|length > 5 %}
|
||||
<a href="javascript:void(0);" id="artistMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#artistMore").click(function (e) {
|
||||
$("span.artist:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if album.genre %}{% trans '流派:' %}{{ album.genre }}{% endif %}</div>
|
||||
|
||||
<div>{% if album.release_date %}{% trans '发行日期:' %}{{ album.release_date}}{% endif %}</div>
|
||||
{% if album.rating %}
|
||||
{% trans '评分: ' %}<span class="entity-card__rating-star rating-star"
|
||||
data-rating-score="{{ album.rating | floatformat:" 0" }}"></span>
|
||||
<span class="entity-card__rating-score rating-score">{{ album.rating }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
91
music/templates/music/create_update_album.html
Normal file
91
music/templates/music/create_update_album.html
Normal file
|
@ -0,0 +1,91 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ title }}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content" class="container">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper" id="main">
|
||||
<a href="{% url 'music:scrape_album' %}"
|
||||
class="single-section-wrapper__link single-section-wrapper__link--secondary">{% trans '>>> 试试一键剽取~ <<<' %}</a>
|
||||
<form class="entity-form" action="{{ submit_url }}" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.media }}
|
||||
{% for field in form %}
|
||||
{% if field.name == 'release_date' %}
|
||||
{{ field.label_tag }}
|
||||
<input type="date" name="{{ field.name }}" id="{{ field.id_for_label }}">
|
||||
{% else %}
|
||||
{% if field.name != 'id' %}
|
||||
{{ field.label_tag }}
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
<input class="button" type="submit" value="{% trans '提交' %}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
// mark required
|
||||
$("#content *[required]").each(function () {
|
||||
$(this).prev().prepend("*");
|
||||
});
|
||||
|
||||
// when source site is this site, hide url input box and populate it with fake url
|
||||
// the backend would update this field
|
||||
if ($("select[name='source_site']").val() == "{{ this_site_enum_value }}") {
|
||||
$("input[name='source_url']").hide();
|
||||
$("label[for='id_source_url']").hide();
|
||||
$("input[name='source_url']").val("https://www.temp.com/" + Date.now() + Math.random());
|
||||
}
|
||||
$("select[name='source_site']").change(function () {
|
||||
let value = $(this).val();
|
||||
if (value == "{{ this_site_enum_value }}") {
|
||||
$("input[name='source_url']").hide();
|
||||
$("label[for='id_source_url']").hide();
|
||||
$("input[name='source_url']").val("https://www.temp.com/" + Date.now() + Math.random());
|
||||
} else {
|
||||
$("input[name='source_url']").show();
|
||||
$("label[for='id_source_url']").show();
|
||||
$("input[name='source_url']").val("");
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
131
music/templates/music/create_update_album_review.html
Normal file
131
music/templates/music/create_update_album_review.html
Normal file
|
@ -0,0 +1,131 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ title }}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'js/create_update_review.js' %}"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper">
|
||||
<div class="entity-card entity-card--horizontal">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'music:retrieve_album' album.id %}">
|
||||
<img src="{{ album.cover.url }}" alt="" class="item-image float-left">
|
||||
</a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper entity-card__info-wrapper--horizontal">
|
||||
|
||||
<h5 class="entity-card__title"><a href="{% url 'music:retrieve_album' album.id %}">
|
||||
{{ album.title }}
|
||||
</a>
|
||||
<a href="{{ album.source_url }}"><a href="{{ album.source_url }}"><span class="source-label source-label__{{ album.source_site }}">{{ album.get_source_site_display }}</span></a></a>
|
||||
</h5>
|
||||
<div>{% if album.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in album.artist %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="artist">{{ artist }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if album.artist|length > 5 %}
|
||||
<a href="javascript:void(0);" id="artistMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#artistMore").click(function (e) {
|
||||
$("span.artist:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if album.genre %}{% trans '流派:' %}{{ album.genre }}{% endif %}</div>
|
||||
|
||||
<div>{% if album.release_date %}{% trans '发行日期:' %}{{ album.release_date}}{% endif %}</div>
|
||||
|
||||
{% if album.rating %}
|
||||
{% trans '评分:' %}<span class="entity-card__rating-star rating-star" data-rating-score="{{ album.rating | floatformat:"0" }}"> </span>
|
||||
<span class="entity-card__rating-score rating-score"> {{ album.rating }} </span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dividing-line"></div>
|
||||
|
||||
<form action="{{ submit_url }}" method="post" class="review-form">
|
||||
{% csrf_token %}
|
||||
{{ form.album }}
|
||||
<div>
|
||||
{{ form.title.label }}
|
||||
</div>
|
||||
{{ form.title }}
|
||||
<div class="clearfix">
|
||||
<span class="float-left">
|
||||
{{ form.content.label }}
|
||||
</span>
|
||||
<span class="float-right">
|
||||
<span class="review-form__preview-button">{% trans '预览' %}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div id="rawContent">
|
||||
{{ form.content }}
|
||||
</div>
|
||||
<div class="review-form__fyi">{% trans '不知道什么是Markdown?可以参考' %}<a target="_blank" href="https://www.markdownguide.org/">{% trans '这里' %}</a></div>
|
||||
<div class="review-form__option">
|
||||
<div class="review-form__visibility-radio">
|
||||
|
||||
{{ form.is_private.label }}{{ form.is_private }}
|
||||
</div>
|
||||
<div class="review-form__share-checkbox">
|
||||
{{ form.share_to_mastodon }}{{ form.share_to_mastodon.label }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix">
|
||||
<input class="button float-right" type="submit" value="{% trans '提交' %}">
|
||||
</div>
|
||||
{{ form.media }}
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
96
music/templates/music/create_update_song.html
Normal file
96
music/templates/music/create_update_song.html
Normal file
|
@ -0,0 +1,96 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ title }}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content" class="container">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper" id="main">
|
||||
|
||||
{% comment %}
|
||||
<a href="{% url 'music:scrape_song' %}"
|
||||
class="single-section-wrapper__link single-section-wrapper__link--secondary">{% trans '>>> 试试一键剽取~ <<<' %}
|
||||
</a>
|
||||
{% endcomment %}
|
||||
|
||||
<form class="entity-form" action="{{ submit_url }}" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.media }}
|
||||
{% for field in form %}
|
||||
{% if field.name == 'release_date' %}
|
||||
{{ field.label_tag }}
|
||||
<input type="date" name="{{ field.name }}" id="{{ field.id_for_label }}">
|
||||
{% else %}
|
||||
{% if field.name != 'id' %}
|
||||
{{ field.label_tag }}
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
<input class="button" type="submit" value="{% trans '提交' %}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
// mark required
|
||||
$("#content *[required]").each(function () {
|
||||
$(this).prev().prepend("*");
|
||||
});
|
||||
|
||||
// when source site is this site, hide url input box and populate it with fake url
|
||||
// the backend would update this field
|
||||
if ($("select[name='source_site']").val() == "{{ this_site_enum_value }}") {
|
||||
$("input[name='source_url']").hide();
|
||||
$("label[for='id_source_url']").hide();
|
||||
$("input[name='source_url']").val("https://www.temp.com/" + Date.now() + Math.random());
|
||||
}
|
||||
$("select[name='source_site']").change(function () {
|
||||
let value = $(this).val();
|
||||
if (value == "{{ this_site_enum_value }}") {
|
||||
$("input[name='source_url']").hide();
|
||||
$("label[for='id_source_url']").hide();
|
||||
$("input[name='source_url']").val("https://www.temp.com/" + Date.now() + Math.random());
|
||||
} else {
|
||||
$("input[name='source_url']").show();
|
||||
$("label[for='id_source_url']").show();
|
||||
$("input[name='source_url']").val("");
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
135
music/templates/music/create_update_song_review.html
Normal file
135
music/templates/music/create_update_song_review.html
Normal file
|
@ -0,0 +1,135 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ title }}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'js/create_update_review.js' %}"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper">
|
||||
<div class="entity-card entity-card--horizontal">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'music:retrieve_song' song.id %}">
|
||||
<img src="{{ song.cover.url }}" alt="" class="item-image float-left">
|
||||
</a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper entity-card__info-wrapper--horizontal">
|
||||
|
||||
<h5 class="entity-card__title"><a href="{% url 'music:retrieve_song' song.id %}">
|
||||
{{ song.title }}
|
||||
</a>
|
||||
<a href="{{ song.source_url }}"><a href="{{ song.source_url }}"><span class="source-label source-label__{{ song.source_site }}">{{ song.get_source_site_display }}</span></a></a>
|
||||
</h5>
|
||||
<div>{% if song.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in song.artist %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="artist">{{ artist }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if song.artist|length > 5 %}
|
||||
<a href="javascript:void(0);" id="artistMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#artistMore").click(function (e) {
|
||||
$("span.artist:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if song.genre %}{% trans '流派:' %}{{ song.genre }}{% endif %}</div>
|
||||
<div>{% if song.album %}{% trans '所属专辑:' %}
|
||||
<a href="{% url 'music:retrieve_album' song.album.id %}">{{ song.album }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>{% if song.release_date %}{% trans '发行日期:' %}{{ song.release_date }}{% endif %}</div>
|
||||
|
||||
{% if song.rating %}
|
||||
{% trans '评分:' %}<span class="entity-card__rating-star rating-star" data-rating-score="{{ song.rating | floatformat:"0" }}"> </span>
|
||||
<span class="entity-card__rating-score rating-score"> {{ song.rating }} </span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dividing-line"></div>
|
||||
|
||||
<form action="{{ submit_url }}" method="post" class="review-form">
|
||||
{% csrf_token %}
|
||||
{{ form.song }}
|
||||
<div>
|
||||
{{ form.title.label }}
|
||||
</div>
|
||||
{{ form.title }}
|
||||
<div class="clearfix">
|
||||
<span class="float-left">
|
||||
{{ form.content.label }}
|
||||
</span>
|
||||
<span class="float-right">
|
||||
<span class="review-form__preview-button">{% trans '预览' %}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div id="rawContent">
|
||||
{{ form.content }}
|
||||
</div>
|
||||
<div class="review-form__fyi">{% trans '不知道什么是Markdown?可以参考' %}<a target="_blank" href="https://www.markdownguide.org/">{% trans '这里' %}</a></div>
|
||||
<div class="review-form__option">
|
||||
<div class="review-form__visibility-radio">
|
||||
|
||||
{{ form.is_private.label }}{{ form.is_private }}
|
||||
</div>
|
||||
<div class="review-form__share-checkbox">
|
||||
{{ form.share_to_mastodon }}{{ form.share_to_mastodon.label }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix">
|
||||
<input class="button float-right" type="submit" value="{% trans '提交' %}">
|
||||
</div>
|
||||
{{ form.media }}
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
104
music/templates/music/delete_album.html
Normal file
104
music/templates/music/delete_album.html
Normal file
|
@ -0,0 +1,104 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - 删除音乐' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper" id="main">
|
||||
<h5>{% trans '确认删除这个作品吗?相关评论和标记将一并删除。' %}</h5>
|
||||
|
||||
<div class="entity-card entity-card--horizontal">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'music:retrieve_album' album.id %}">
|
||||
<img src="{{ album.cover.url }}" alt="" class="item-image float-left">
|
||||
</a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper entity-card__info-wrapper--horizontal">
|
||||
<a href="{% url 'music:retrieve_album' album.id %}">
|
||||
<h5 class="entity-card__title">
|
||||
{{ album.title }}
|
||||
<a href="{{ album.source_url }}"><span class="source-label source-label__{{ album.source_site }}">{{ album.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
</a>
|
||||
{% if album.rating %}
|
||||
{% trans '评分:' %}<span class="entity-card__rating-star rating-star" data-rating-score="{{ album.rating | floatformat:"0" }}">
|
||||
</span>
|
||||
<span class="entity-card__rating-score">{{ album.rating }}</span>
|
||||
{% else %}
|
||||
<span>{% trans '评分:暂无评分' %}</span>
|
||||
{% endif %}
|
||||
|
||||
{% if album.last_editor %}
|
||||
<div>
|
||||
{% trans '最近编辑者:' %}
|
||||
<a href="{% url 'users:home' album.last_editor.id %}">
|
||||
<span>{{ album.last_editor | default:"" }}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div>{% trans '上次编辑时间:' %}{{ album.edited_time }}</div>
|
||||
|
||||
{% if album.album_marks.all %}
|
||||
<div><strong>{% trans '这个条目有' %} <a href="javascript:void();">{{ album.album_marks.count }}</a> 个标记</strong></div>
|
||||
{% endif %}
|
||||
{% if album.album_reviews.all %}
|
||||
<div><strong>{% trans '这个条目有' %} <a href="javascript:void();">{{ album.album_reviews.count }}</a> 个评论</strong></div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="dividing-line"></div>
|
||||
<div class="clearfix">
|
||||
<form action="{% url 'music:delete_album' album.id %}" method="post" class="float-right">
|
||||
{% csrf_token %}
|
||||
<input class="button" type="submit" value="{% trans '确认' %}">
|
||||
</form>
|
||||
<button onclick="history.back()" class="button button-clear float-right">{% trans '返回' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
108
music/templates/music/delete_album_review.html
Normal file
108
music/templates/music/delete_album_review.html
Normal file
|
@ -0,0 +1,108 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - 删除评论' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper" id="main">
|
||||
<h5>{% trans '确认删除这篇评论吗?' %}</h5>
|
||||
|
||||
<div class="dividing-line"></div>
|
||||
|
||||
|
||||
<div class="review-head">
|
||||
|
||||
<h5 class="review-head__title">
|
||||
{{ review.title }}
|
||||
</h5>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z" />
|
||||
</svg></span>
|
||||
{% endif %}
|
||||
|
||||
<div class="review-head__body">
|
||||
<div class="review-head__info">
|
||||
|
||||
<a href="{% url 'users:home' review.owner.id %}"
|
||||
class="review-head__owner-link">{{ review.owner.username }}</a>
|
||||
|
||||
{% if mark %}
|
||||
|
||||
{% if mark.rating %}
|
||||
<span class="review-head__rating-star rating-star"
|
||||
data-rating-score="{{ mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
<span class="review-head__time">{{ review.edited_time }}</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div id="rawContent" class="delete-preview">
|
||||
{{ form.content }}
|
||||
</div>
|
||||
{{ form.media }}
|
||||
|
||||
<div class="dividing-line"></div>
|
||||
|
||||
<div class="clearfix">
|
||||
<form action="{% url 'music:delete_album_review' review.id %}" method="post" class="float-right">
|
||||
{% csrf_token %}
|
||||
<input class="button" type="submit" value="{% trans '确认' %}">
|
||||
</form>
|
||||
<button onclick="history.back()"
|
||||
class="button button-clear float-right">{% trans '返回' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
$(".markdownx textarea").hide();
|
||||
$(".markdownx .markdownx-preview").show();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
104
music/templates/music/delete_song.html
Normal file
104
music/templates/music/delete_song.html
Normal file
|
@ -0,0 +1,104 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - 删除音乐' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper" id="main">
|
||||
<h5>{% trans '确认删除这个作品吗?相关评论和标记将一并删除。' %}</h5>
|
||||
|
||||
<div class="entity-card entity-card--horizontal">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'music:retrieve_song' song.id %}">
|
||||
<img src="{{ song.cover.url }}" alt="" class="item-image float-left">
|
||||
</a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper entity-card__info-wrapper--horizontal">
|
||||
<a href="{% url 'music:retrieve_song' song.id %}">
|
||||
<h5 class="entity-card__title">
|
||||
{{ song.title }}
|
||||
<a href="{{ song.source_url }}"><span class="source-label source-label__{{ song.source_site }}">{{ song.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
</a>
|
||||
{% if song.rating %}
|
||||
{% trans '评分:' %}<span class="entity-card__rating-star rating-star" data-rating-score="{{ song.rating | floatformat:"0" }}">
|
||||
</span>
|
||||
<span class="entity-card__rating-score">{{ song.rating }}</span>
|
||||
{% else %}
|
||||
<span>{% trans '评分:暂无评分' %}</span>
|
||||
{% endif %}
|
||||
|
||||
{% if song.last_editor %}
|
||||
<div>
|
||||
{% trans '最近编辑者:' %}
|
||||
<a href="{% url 'users:home' song.last_editor.id %}">
|
||||
<span>{{ song.last_editor | default:"" }}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div>{% trans '上次编辑时间:' %}{{ song.edited_time }}</div>
|
||||
|
||||
{% if song.song_marks.all %}
|
||||
<div><strong>{% trans '这个条目有' %} <a href="javascript:void();">{{ song.song_marks.count }}</a> 个标记</strong></div>
|
||||
{% endif %}
|
||||
{% if song.song_reviews.all %}
|
||||
<div><strong>{% trans '这个条目有' %} <a href="javascript:void();">{{ song.song_reviews.count }}</a> 个评论</strong></div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="dividing-line"></div>
|
||||
<div class="clearfix">
|
||||
<form action="{% url 'music:delete_song' song.id %}" method="post" class="float-right">
|
||||
{% csrf_token %}
|
||||
<input class="button" type="submit" value="{% trans '确认' %}">
|
||||
</form>
|
||||
<button onclick="history.back()" class="button button-clear float-right">{% trans '返回' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
108
music/templates/music/delete_song_review.html
Normal file
108
music/templates/music/delete_song_review.html
Normal file
|
@ -0,0 +1,108 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - 删除评论' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper" id="main">
|
||||
<h5>{% trans '确认删除这篇评论吗?' %}</h5>
|
||||
|
||||
<div class="dividing-line"></div>
|
||||
|
||||
|
||||
<div class="review-head">
|
||||
|
||||
<h5 class="review-head__title">
|
||||
{{ review.title }}
|
||||
</h5>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z" />
|
||||
</svg></span>
|
||||
{% endif %}
|
||||
|
||||
<div class="review-head__body">
|
||||
<div class="review-head__info">
|
||||
|
||||
<a href="{% url 'users:home' review.owner.id %}"
|
||||
class="review-head__owner-link">{{ review.owner.username }}</a>
|
||||
|
||||
{% if mark %}
|
||||
|
||||
{% if mark.rating %}
|
||||
<span class="review-head__rating-star rating-star"
|
||||
data-rating-score="{{ mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
<span class="review-head__time">{{ review.edited_time }}</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div id="rawContent" class="delete-preview">
|
||||
{{ form.content }}
|
||||
</div>
|
||||
{{ form.media }}
|
||||
|
||||
<div class="dividing-line"></div>
|
||||
|
||||
<div class="clearfix">
|
||||
<form action="{% url 'music:delete_song_review' review.id %}" method="post" class="float-right">
|
||||
{% csrf_token %}
|
||||
<input class="button" type="submit" value="{% trans '确认' %}">
|
||||
</form>
|
||||
<button onclick="history.back()"
|
||||
class="button button-clear float-right">{% trans '返回' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
$(".markdownx textarea").hide();
|
||||
$(".markdownx .markdownx-preview").show();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
109
music/templates/music/scrape_album.html
Normal file
109
music/templates/music/scrape_album.html
Normal file
|
@ -0,0 +1,109 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - 从豆瓣获取数据' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'js/scrape.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style>
|
||||
#scrape {
|
||||
overflow: auto;
|
||||
}
|
||||
#scrape iframe {
|
||||
width: 100%;
|
||||
}
|
||||
#scrape textarea {
|
||||
height: 200px;
|
||||
resize: vertical;
|
||||
}
|
||||
#scrape iframe {
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid grid--reverse-order">
|
||||
<div class="grid__main grid__main--reverse-order" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div id="scrape">
|
||||
<h5>
|
||||
{% trans '根据豆瓣内容填写下方表单' %}
|
||||
</h5>
|
||||
<iframe id='test' sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="https://search.douban.com/music/subject_search{% if q %}?search_text={{ q }}{% endif %}" frameborder="0"></iframe>
|
||||
<div class="dividing-line"></div>
|
||||
<div id="scrapeForm">
|
||||
<form action="{% url 'music:create_album' %}" method="POST" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form.media }}
|
||||
|
||||
{% for field in form %}
|
||||
|
||||
{% if field.id_for_label == 'id_is_series' %}
|
||||
<label for="{{ field.id_for_label }}" style="display: inline-block; position: relative; left: -4px;">{{ field.label }}</label>
|
||||
{{ field }}
|
||||
{% else %}
|
||||
{% if field.id_for_label != 'id_id' %}
|
||||
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</form>
|
||||
<a href="#" class="button add-button submit">{% trans '剽取!' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside grid__aside--reverse-order" id="aside">
|
||||
<div class="aside-section-wrapper aside-section-wrapper--singular">
|
||||
<h5>
|
||||
{% trans '复制详情页链接' %}
|
||||
</h5>
|
||||
<form action="{% url 'music:click_to_scrape_album' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="text" name="url" required placeholder="https://music.douban.com/subject/1000000/">
|
||||
<input type="submit" class="button add-button" value="{% trans '一键剽取!' %}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
|
||||
</div>
|
||||
<script>
|
||||
// mark required
|
||||
$("#content *[required]").each(function () {
|
||||
$(this).prev().prepend("*");
|
||||
});
|
||||
$('form').submit(function () {
|
||||
$(this).find("input[type='submit']").prop('disabled', true);
|
||||
$(this).find("button[type='submit']").prop('disabled', true);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
109
music/templates/music/scrape_song.html
Normal file
109
music/templates/music/scrape_song.html
Normal file
|
@ -0,0 +1,109 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - 从豆瓣获取数据' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'js/scrape.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style>
|
||||
#scrape {
|
||||
overflow: auto;
|
||||
}
|
||||
#scrape iframe {
|
||||
width: 100%;
|
||||
}
|
||||
#scrape textarea {
|
||||
height: 200px;
|
||||
resize: vertical;
|
||||
}
|
||||
#scrape iframe {
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid grid--reverse-order">
|
||||
<div class="grid__main grid__main--reverse-order" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div id="scrape">
|
||||
<h5>
|
||||
{% trans '根据豆瓣内容填写下方表单' %}
|
||||
</h5>
|
||||
<iframe id='test' sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="https://search.douban.com/movie/subject_search{% if q %}?search_text={{ q }}{% endif %}" frameborder="0"></iframe>
|
||||
<div class="dividing-line"></div>
|
||||
<div id="scrapeForm">
|
||||
<form action="{% url 'movies:create' %}" method="POST" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form.media }}
|
||||
|
||||
{% for field in form %}
|
||||
|
||||
{% if field.id_for_label == 'id_is_series' %}
|
||||
<label for="{{ field.id_for_label }}" style="display: inline-block; position: relative; left: -4px;">{{ field.label }}</label>
|
||||
{{ field }}
|
||||
{% else %}
|
||||
{% if field.id_for_label != 'id_id' %}
|
||||
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</form>
|
||||
<a href="#" class="button add-button submit">{% trans '剽取!' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside grid__aside--reverse-order" id="aside">
|
||||
<div class="aside-section-wrapper aside-section-wrapper--singular">
|
||||
<h5>
|
||||
{% trans '复制详情页链接' %}
|
||||
</h5>
|
||||
<form action="{% url 'movies:click_to_scrape' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="text" name="url" required placeholder="https://movie.douban.com/subject/1000000/">
|
||||
<input type="submit" class="button add-button" value="{% trans '一键剽取!' %}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
|
||||
</div>
|
||||
<script>
|
||||
// mark required
|
||||
$("#content *[required]").each(function () {
|
||||
$(this).prev().prepend("*");
|
||||
});
|
||||
$('form').submit(function () {
|
||||
$(this).find("input[type='submit']").prop('disabled', true);
|
||||
$(this).find("button[type='submit']").prop('disabled', true);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
402
music/templates/music/song_detail.html
Normal file
402
music/templates/music/song_detail.html
Normal file
|
@ -0,0 +1,402 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load strip_scheme %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:title" content="NiceDB音乐 - {{ song.title }}">
|
||||
<meta property="og:type" content="music.song">
|
||||
<meta property="og:url" content="{{ request.build_absolute_uri }}">
|
||||
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{{ song.cover.url }}">
|
||||
<meta property="og:site_name" content="NiceDB">
|
||||
<meta property="og:description"content="{{ song.brief }}">
|
||||
|
||||
<title>{% trans 'NiceDB - 音乐详情' %} | {{ song.title }}</title>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/detail.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="entity-detail">
|
||||
|
||||
<img src="{{ song.cover.url }}" class="entity-detail__img" alt="{{ song.title }}">
|
||||
|
||||
<div class="entity-detail__info">
|
||||
<h5 class="entity-detail__title">
|
||||
|
||||
{{ song.title }}
|
||||
<a href="{{ song.source_url }}"><span class="source-label source-label__{{ song.source_site }}">{{ song.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
|
||||
<div class="entity-detail__fields">
|
||||
<div class="entity-detail__rating">
|
||||
{% if song.rating %}
|
||||
<span class="entity-detail__rating-star rating-star" data-rating-score="{{ song.rating | floatformat:"0" }}"></span>
|
||||
<span class="entity-detail__rating-score"> {{ song.rating }} </span>
|
||||
{% else %}
|
||||
<span> {% trans '评分:暂无评分' %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if song.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in song.artist %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="artist">{{ artist }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if song.artist|length > 5 %}
|
||||
<a href="javascript:void(0);" id="artistMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#artistMore").click(function (e) {
|
||||
$("span.artist:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}</div>
|
||||
<div>{% if song.release_date %}
|
||||
{% trans '发行日期:' %}{{ song.release_date }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if song.duration %}
|
||||
{% trans '时长:' %}{{ song.get_duration_display }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if song.genre %}
|
||||
{% trans '流派:' %}{{ song.genre }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="entity-detail__fields">
|
||||
<div>{% if song.isrc %}
|
||||
{% trans 'ISRC:' %}{{ song.isrc }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if song.album %}
|
||||
{% trans '所属专辑:' %}<a href="{% url 'music:retrieve_album' song.album.id %}">{{ song.album }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if song.other_info %}
|
||||
{% for k, v in song.other_info.items %}
|
||||
<div>
|
||||
{{k}}:{{v}}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if song.last_editor %}
|
||||
<div>{% trans '最近编辑者:' %}<a href="{% url 'users:home' song.last_editor.id %}">{{ song.last_editor | default:"" }}</a></div>
|
||||
{% endif %}
|
||||
|
||||
<div>
|
||||
<a href="{% url 'music:update_song' song.id %}">{% trans '编辑这个作品' %}</a>
|
||||
{% if user.is_staff %}
|
||||
/<a href="{% url 'music:delete_song' song.id %}"> {% trans '删除' %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tag-collection">
|
||||
|
||||
{% for tag_dict in song_tag_list %}
|
||||
{% for k, v in tag_dict.items %}
|
||||
{% if k == 'content' %}
|
||||
<span class="tag-collection__tag">
|
||||
<a href="{% url 'common:search' %}?tag={{ v }}">{{ v }}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dividing-line"></div>
|
||||
<div class="entity-desc" id="description">
|
||||
<h5 class="entity-desc__title">{% trans '简介' %}</h5>
|
||||
{% if song.brief %}
|
||||
|
||||
<p class="entity-desc__content">{{ song.brief | linebreaksbr }}</p>
|
||||
<div class="entity-desc__unfold-button entity-desc__unfold-button--hidden">
|
||||
<a href="javascript:void(0);">展开全部</a>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div>{% trans '暂无简介' %}</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="entity-marks">
|
||||
|
||||
<h5 class="entity-marks__title">{% trans '这部作品的标记' %}</h5>
|
||||
{% if mark_list_more %}
|
||||
<a href="{% url 'music:retrieve_song_mark_list' song.id %}" class="entity-marks__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
{% if mark_list %}
|
||||
<ul class="entity-marks__mark-list">
|
||||
{% for others_mark in mark_list %}
|
||||
<li class="entity-marks__mark">
|
||||
<a href="{% url 'users:home' others_mark.owner.id %}" class="entity-marks__owner-link">{{ others_mark.owner.username }}</a>
|
||||
<span>{{ others_mark.get_status_display }}</span>
|
||||
{% if others_mark.rating %}
|
||||
<span class="entity-marks__rating-star rating-star" data-rating-score="{{ others_mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
{% if others_mark.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-marks__mark-time">{{ others_mark.edited_time }}</span>
|
||||
{% if others_mark.text %}
|
||||
<p class="entity-marks__mark-content">{{ others_mark.text }}</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div>{% trans '暂无标记' %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="entity-reviews">
|
||||
<h5 class="entity-reviews__title">{% trans '这部作品的评论' %}</h5>
|
||||
|
||||
{% if review_list_more %}
|
||||
<a href="{% url 'music:retrieve_song_review_list' song.id %}" class="entity-reviews__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
{% if review_list %}
|
||||
<ul class="entity-reviews__review-list">
|
||||
{% for others_review in review_list %}
|
||||
<li class="entity-reviews__review">
|
||||
<a href="{% url 'users:home' others_review.owner.id %}" class="entity-reviews__owner-link">{{ others_review.owner.username }}</a>
|
||||
{% if others_review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-reviews__review-time">{{ others_review.edited_time }}</span>
|
||||
<span class="entity-reviews__review-title"> <a href="{% url 'music:retrieve_song_review' others_review.id %}">{{ others_review.title }}</a></span>
|
||||
<span>{{ others_review.get_plain_content | truncate:100 }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div>{% trans '暂无评论' %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
|
||||
{% if mark %}
|
||||
<div class="mark-panel">
|
||||
|
||||
<span class="mark-panel__status">{% trans '我' %}{{ mark.get_status_display }}</span>
|
||||
{% if mark.status == status_enum.DO.value or mark.status == status_enum.COLLECT.value%}
|
||||
{% if mark.rating %}
|
||||
<span class="mark-panel__rating-star rating-star" data-rating-score="{{ mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if mark.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
<span class="mark-panel__actions">
|
||||
<a href="" class="edit">{% trans '修改' %}</a>
|
||||
<form action="{% url 'music:delete_song_mark' mark.id %}" method="post">
|
||||
{% csrf_token %}
|
||||
<a href="" class="delete">{% trans '删除' %}</a>
|
||||
</form>
|
||||
</span>
|
||||
<div class="mark-panel__clear"></div>
|
||||
|
||||
<div class="mark-panel__time">{{ mark.edited_time }}</div>
|
||||
|
||||
{% if mark.text %}
|
||||
<p class="mark-panel__text">{{ mark.text }}</p>
|
||||
{% endif %}
|
||||
<div class="tag-collection">
|
||||
|
||||
{% for tag in mark_tags %}
|
||||
<span class="tag-collection__tag">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="action-panel" id="addMarkPanel">
|
||||
<div class="action-panel__label">{% trans '标记这部作品' %}</div>
|
||||
<div class="action-panel__button-group">
|
||||
<button class="action-panel__button" data-status="{{ status_enum.WISH.value }}" id="wishButton">{% trans '想看' %}</button>
|
||||
<button class="action-panel__button" data-status="{{ status_enum.DO.value }}">{% trans '在看' %}</button>
|
||||
<button class="action-panel__button" data-status="{{ status_enum.COLLECT.value }}">{% trans '看过' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="aside-section-wrapper">
|
||||
{% if review %}
|
||||
<div class="review-panel">
|
||||
|
||||
<span class="review-panel__label">{% trans '我的评论' %}</span>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
|
||||
<span class="review-panel__actions">
|
||||
<a href="{% url 'music:update_song_review' review.id %}">{% trans '编辑' %}</a>
|
||||
<a href="{% url 'music:delete_song_review' review.id %}">{% trans '删除' %}</a>
|
||||
</span>
|
||||
|
||||
<div class="review-panel__time">{{ review.edited_time }}</div>
|
||||
|
||||
<a href="{% url 'music:retrieve_song_review' review.id %}" class="review-panel__review-title">
|
||||
{{ review.title }}
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
<div class="action-panel">
|
||||
<div class="action-panel__label">{% trans '我的评论' %}</div>
|
||||
<div class="action-panel__button-group action-panel__button-group--center">
|
||||
|
||||
<a href="{% url 'music:create_song_review' song.id %}">
|
||||
<button class="action-panel__button">{% trans '去写评论' %}</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
<div id="modals">
|
||||
<div class="mark-modal modal">
|
||||
<div class="mark-modal__head">
|
||||
|
||||
{% if not mark %}
|
||||
|
||||
<style>
|
||||
.mark-modal__title::after {
|
||||
content: "{% trans '这部作品' %}";
|
||||
}
|
||||
</style>
|
||||
|
||||
<span class="mark-modal__title"></span>
|
||||
{% else %}
|
||||
<span class="mark-modal__title">{% trans '我的标记' %}</span>
|
||||
{% endif %}
|
||||
|
||||
<span class="mark-modal__close-button modal-close">
|
||||
<span class="icon-cross">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<polygon
|
||||
points="20 2.61 17.39 0 10 7.39 2.61 0 0 2.61 7.39 10 0 17.39 2.61 20 10 12.61 17.39 20 20 17.39 12.61 10 20 2.61">
|
||||
</polygon>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mark-modal__body">
|
||||
<form action="{% url 'music:create_update_song_mark' %}" method="post">
|
||||
{{ mark_form.media }}
|
||||
{% csrf_token %}
|
||||
{{ mark_form.id }}
|
||||
{{ mark_form.song }}
|
||||
{% if mark.rating %}
|
||||
{% endif %}
|
||||
|
||||
<div class="mark-modal__rating-star rating-star-edit"></div>
|
||||
{{ mark_form.rating }}
|
||||
<div id="statusSelection" class="mark-modal__status-radio" {% if not mark %}hidden{% endif %}>
|
||||
{{ mark_form.status }}
|
||||
</div>
|
||||
|
||||
<div class="mark-modal__clear"></div>
|
||||
|
||||
{{ mark_form.text }}
|
||||
|
||||
<div class="mark-modal__tag">
|
||||
<label>{{ mark_form.tags.label }}</label>
|
||||
{{ mark_form.tags }}
|
||||
</div>
|
||||
|
||||
<div class="mark-modal__option">
|
||||
<div class="mark-modal__visibility-radio">
|
||||
<span>{{ mark_form.is_private.label }}:</span>
|
||||
{{ mark_form.is_private }}
|
||||
</div>
|
||||
<div class="mark-modal__share-checkbox">
|
||||
{{ mark_form.share_to_mastodon }}{{ mark_form.share_to_mastodon.label }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mark-modal__confirm-button">
|
||||
<input type="submit" class="button float-right" value="{% trans '提交' %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="confirm-modal modal">
|
||||
<div class="confirm-modal__head">
|
||||
<span class="confirm-modal__title">{% trans '确定要删除你的标记吗?' %}</span>
|
||||
<span class="confirm-modal__close-button modal-close">
|
||||
<span class="icon-cross">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<polygon
|
||||
points="20 2.61 17.39 0 10 7.39 2.61 0 0 2.61 7.39 10 0 17.39 2.61 20 10 12.61 17.39 20 20 17.39 12.61 10 20 2.61">
|
||||
</polygon>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="confirm-modal__body">
|
||||
<div class="confirm-modal__confirm-button">
|
||||
<input type="submit" class="button float-right" value="{% trans '确认' %}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-mask"></div>
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
176
music/templates/music/song_mark_list.html
Normal file
176
music/templates/music/song_mark_list.html
Normal file
|
@ -0,0 +1,176 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load highlight %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ song.title }}{% trans '的标记' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="entity-marks">
|
||||
<h5 class="entity-marks__title entity-marks__title--stand-alone">
|
||||
<a href="{% url 'music:retrieve_song' song.id %}">{{ song.title }}</a>{% trans '
|
||||
的标记' %}
|
||||
</h5>
|
||||
<ul class="entity-marks__mark-list">
|
||||
|
||||
{% for mark in marks %}
|
||||
|
||||
<li class="entity-marks__mark entity-marks__mark--wider">
|
||||
<a href="{% url 'users:home' mark.owner.id %}"
|
||||
class="entity-marks__owner-link">{{ mark.owner.username }}</a>
|
||||
<span>{{ mark.get_status_display }}</span>
|
||||
{% if mark.rating %}
|
||||
<span class="entity-marks__rating-star rating-star"
|
||||
data-rating-score="{{ mark.rating | floatformat:" 0" }}"></span>
|
||||
{% endif %}
|
||||
{% if mark.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z" />
|
||||
</svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-marks__mark-time">{{ mark.edited_time }}</span>
|
||||
{% if mark.text %}
|
||||
<p class="entity-marks__mark-content">{{ mark.text }}</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
{% empty %}
|
||||
<div>
|
||||
{% trans '无结果' %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
|
||||
{% if marks.pagination.has_prev %}
|
||||
<a href="?page=1" class="pagination__nav-link pagination__nav-link">«</a>
|
||||
<a href="?page={{ marks.previous_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">‹</a>
|
||||
{% endif %}
|
||||
|
||||
{% for page in marks.pagination.page_range %}
|
||||
|
||||
{% if page == marks.pagination.current_page %}
|
||||
<a href="?page={{ page }}"
|
||||
class="pagination__page-link pagination__page-link--current">{{ page }}</a>
|
||||
{% else %}
|
||||
<a href="?page={{ page }}" class="pagination__page-link">{{ page }}</a>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% if marks.pagination.has_next %}
|
||||
<a href="?page={{ marks.next_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--left-margin">›</a>
|
||||
<a href="?page={{ marks.pagination.last_page }}"
|
||||
class="pagination__nav-link">»</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
<div class="entity-card">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'music:retrieve_song' song.id %}"><img src="{{ song.cover.url }}"
|
||||
alt="" class="entity-card__img"></a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper">
|
||||
<h5 class="entity-card__title"><a href="{% url 'music:retrieve_song' song.id %}">
|
||||
{{ song.title }}
|
||||
</a>
|
||||
<a href="{{ song.source_url }}"><span
|
||||
class="source-label source-label__{{ song.source_site }}">{{
|
||||
song.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
|
||||
<div>{% if song.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in song.artist %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="artist">{{ artist }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if song.artist|length > 5 %}
|
||||
<a href="javascript:void(0);" id="artistMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#artistMore").click(function (e) {
|
||||
$("span.artist:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if song.genre %}{% trans '流派:' %}{{ song.genre }}{% endif %}</div>
|
||||
<div>{% if song.album %}{% trans '所属专辑:' %}
|
||||
<a href="{% url 'music:retrieve_album' song.album.id %}">{{ song.album }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>{% if song.release_date %}{% trans '发行日期:' %}{{ song.release_date }}{% endif %}
|
||||
</div>
|
||||
{% if song.rating %}
|
||||
{% trans '评分: ' %}<span class="entity-card__rating-star rating-star"
|
||||
data-rating-score="{{ song.rating | floatformat:" 0" }}"></span>
|
||||
<span class="entity-card__rating-score rating-score">{{ song.rating }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
153
music/templates/music/song_review_detail.html
Normal file
153
music/templates/music/song_review_detail.html
Normal file
|
@ -0,0 +1,153 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:title" content="NiceDB乐评 - {{ review.title }}">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:article:author" content="{{ review.owner.username }}">
|
||||
<meta property="og:url" content="{{ request.build_absolute_uri }}">
|
||||
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{% static 'img/logo_square.svg' %}">
|
||||
<title>{% trans 'NiceDB - 评论详情' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="review-head">
|
||||
<h5 class="review-head__title">
|
||||
{{ review.title }}
|
||||
</h5>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><svg
|
||||
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z" />
|
||||
</svg></span>
|
||||
{% endif %}
|
||||
<div class="review-head__body">
|
||||
<div class="review-head__info">
|
||||
|
||||
<a href="{% url 'users:home' review.owner.id %}" class="review-head__owner-link">{{ review.owner.username }}</a>
|
||||
|
||||
{% if mark %}
|
||||
|
||||
{% if mark.rating %}
|
||||
<span class="review-head__rating-star rating-star" data-rating-score="{{ mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
<span class="review-head__time">{{ review.edited_time }}</span>
|
||||
|
||||
</div>
|
||||
<div class="review-head__actions">
|
||||
{% if request.user == review.owner %}
|
||||
<a class="review-head__action-link" href="{% url 'music:update_song_review' review.id %}">{% trans '编辑' %}</a>
|
||||
<a class="review-head__action-link" href="{% url 'music:delete_song_review' review.id %}">{% trans '删除' %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="dividing-line"></div> -->
|
||||
<div id="rawContent">
|
||||
{{ form.content }}
|
||||
</div>
|
||||
{{ form.media }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
<div class="entity-card">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'music:retrieve_song' song.id %}"><img src="{{ song.cover.url }}" alt=""
|
||||
class="entity-card__img"></a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper">
|
||||
<h5 class="entity-card__title"><a href="{% url 'music:retrieve_song' song.id %}">
|
||||
{{ song.title }}
|
||||
</a>
|
||||
<a href="{{ song.source_url }}"><span class="source-label source-label__{{ song.source_site }}">{{ song.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
|
||||
<div>{% if song.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in song.artist %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="artist">{{ artist }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if song.artist|length > 5 %}
|
||||
<a href="javascript:void(0);" id="artistMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#artistMore").click(function (e) {
|
||||
$("span.artist:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if song.genre %}{% trans '流派:' %}{{ song.genre }}{% endif %}</div>
|
||||
<div>{% if song.album %}{% trans '所属专辑:' %}
|
||||
<a href="{% url 'music:retrieve_album' song.album.id %}">{{ song.album }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>{% if song.release_date %}{% trans '发行日期:' %}{{ song.release_date }}{% endif %}</div>
|
||||
{% if song.rating %}
|
||||
{% trans '评分: ' %}<span class="entity-card__rating-star rating-star"
|
||||
data-rating-score="{{ song.rating | floatformat:"0" }}"></span>
|
||||
<span class="entity-card__rating-score rating-score">{{ song.rating }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
$(".markdownx textarea").hide();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
158
music/templates/music/song_review_list.html
Normal file
158
music/templates/music/song_review_list.html
Normal file
|
@ -0,0 +1,158 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load highlight %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ song.title }}{% trans '的评论' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="entity-reviews">
|
||||
<h5 class="entity-reviews__title entity-reviews__title--stand-alone">
|
||||
<a href="{% url 'music:retrieve_song' song.id %}">{{ song.title }}</a>{% trans ' 的评论' %}
|
||||
</h5>
|
||||
<ul class="entity-reviews__review-list">
|
||||
|
||||
{% for review in reviews %}
|
||||
|
||||
<li class="entity-reviews__review entity-reviews__review--wider">
|
||||
|
||||
<a href="{% url 'users:home' review.owner.id %}" class="entity-reviews__owner-link">{{ review.owner.username }}</a>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-reviews__review-time">{{ review.edited_time }}</span>
|
||||
|
||||
|
||||
<span href="{% url 'music:retrieve_song_review' review.id %}" class="entity-reviews__review-title"><a href="{% url 'music:retrieve_song_review' review.id %}">{{ review.title }}</a></span>
|
||||
|
||||
</li>
|
||||
{% empty %}
|
||||
<div>{% trans '无结果' %}</div>
|
||||
{% endfor %}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
|
||||
{% if reviews.pagination.has_prev %}
|
||||
<a href="?page=1" class="pagination__nav-link pagination__nav-link">«</a>
|
||||
<a href="?page={{ reviews.previous_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">‹</a>
|
||||
{% endif %}
|
||||
|
||||
{% for page in reviews.pagination.page_range %}
|
||||
|
||||
{% if page == reviews.pagination.current_page %}
|
||||
<a href="?page={{ page }}" class="pagination__page-link pagination__page-link--current">{{ page }}</a>
|
||||
{% else %}
|
||||
<a href="?page={{ page }}" class="pagination__page-link">{{ page }}</a>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% if reviews.pagination.has_next %}
|
||||
<a href="?page={{ reviews.next_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--left-margin">›</a>
|
||||
<a href="?page={{ reviews.pagination.last_page }}" class="pagination__nav-link">»</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
<div class="entity-card">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'music:retrieve_song' song.id %}"><img src="{{ song.cover.url }}" alt=""
|
||||
class="entity-card__img"></a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper">
|
||||
<h5 class="entity-card__title"><a href="{% url 'music:retrieve_song' song.id %}">
|
||||
{{ song.title }}
|
||||
</a>
|
||||
<a href="{{ song.source_url }}"><span class="source-label source-label__{{ song.source_site }}">{{ song.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
|
||||
<div>{% if song.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in song.artist %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="artist">{{ artist }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if song.artist|length > 5 %}
|
||||
<a href="javascript:void(0);" id="artistMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#artistMore").click(function (e) {
|
||||
$("span.artist:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if song.genre %}{% trans '流派:' %}{{ song.genre }}{% endif %}</div>
|
||||
<div>{% if song.album %}{% trans '所属专辑:' %}
|
||||
<a href="{% url 'music:retrieve_album' song.album.id %}">{{ song.album }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>{% if song.release_date %}{% trans '发行日期:' %}{{ song.release_date }}{% endif %}</div>
|
||||
{% if song.rating %}
|
||||
{% trans '评分: ' %}<span class="entity-card__rating-star rating-star"
|
||||
data-rating-score="{{ song.rating | floatformat:"0" }}"></span>
|
||||
<span class="entity-card__rating-score rating-score">{{ song.rating }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
3
music/tests.py
Normal file
3
music/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
40
music/urls.py
Normal file
40
music/urls.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
from django.urls import path
|
||||
from .views import *
|
||||
|
||||
|
||||
app_name = 'music'
|
||||
urlpatterns = [
|
||||
path('song/create/', create_song, name='create_song'),
|
||||
path('song/<int:id>/', retrieve_song, name='retrieve_song'),
|
||||
path('song/update/<int:id>/', update_song, name='update_song'),
|
||||
path('song/delete/<int:id>/', delete_song, name='delete_song'),
|
||||
path('song/mark/', create_update_song_mark, name='create_update_song_mark'),
|
||||
path('song/<int:song_id>/mark/list/',
|
||||
retrieve_song_mark_list, name='retrieve_song_mark_list'),
|
||||
path('song/mark/delete/<int:id>/', delete_song_mark, name='delete_song_mark'),
|
||||
path('song/<int:song_id>/review/create/', create_song_review, name='create_song_review'),
|
||||
path('song/review/update/<int:id>/', update_song_review, name='update_song_review'),
|
||||
path('song/review/delete/<int:id>/', delete_song_review, name='delete_song_review'),
|
||||
path('song/review/<int:id>/', retrieve_song_review, name='retrieve_song_review'),
|
||||
path('song/<int:song_id>/review/list/',
|
||||
retrieve_song_review_list, name='retrieve_song_review_list'),
|
||||
# path('song/scrape/', scrape_song, name='scrape_song'),
|
||||
path('song/click_to_scrape/', click_to_scrape_song, name='click_to_scrape_song'),
|
||||
|
||||
path('album/create/', create_album, name='create_album'),
|
||||
path('album/<int:id>/', retrieve_album, name='retrieve_album'),
|
||||
path('album/update/<int:id>/', update_album, name='update_album'),
|
||||
path('album/delete/<int:id>/', delete_album, name='delete_album'),
|
||||
path('album/mark/', create_update_album_mark, name='create_update_album_mark'),
|
||||
path('album/<int:album_id>/mark/list/',
|
||||
retrieve_album_mark_list, name='retrieve_album_mark_list'),
|
||||
path('album/mark/delete/<int:id>/', delete_album_mark, name='delete_album_mark'),
|
||||
path('album/<int:album_id>/review/create/', create_album_review, name='create_album_review'),
|
||||
path('album/review/update/<int:id>/', update_album_review, name='update_album_review'),
|
||||
path('album/review/delete/<int:id>/', delete_album_review, name='delete_album_review'),
|
||||
path('album/review/<int:id>/', retrieve_album_review, name='retrieve_album_review'),
|
||||
path('album/<int:album_id>/review/list/',
|
||||
retrieve_album_review_list, name='retrieve_album_review_list'),
|
||||
path('album/scrape/', scrape_album, name='scrape_album'),
|
||||
path('album/click_to_scrape/', click_to_scrape_album, name='click_to_scrape_album'),
|
||||
]
|
1183
music/views.py
Normal file
1183
music/views.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -108,6 +108,14 @@ def register(request):
|
|||
elif request.method == 'POST':
|
||||
token = request.session['new_user_token']
|
||||
user_data = get_user_data(request.COOKIES['mastodon_domain'], token)
|
||||
if user_data is None:
|
||||
return render(
|
||||
request,
|
||||
'common/error.html',
|
||||
{
|
||||
'msg': _("长毛象访问失败😫")
|
||||
}
|
||||
)
|
||||
new_user = User(
|
||||
username=user_data['username'],
|
||||
mastodon_id=user_data['id'],
|
||||
|
|
Loading…
Add table
Reference in a new issue