Implement basic Music functions

This commit is contained in:
doubaniux 2021-02-12 19:23:23 +01:00
parent bb311a2a92
commit e5bc7f55c0
49 changed files with 4957 additions and 159 deletions

View file

@ -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')),

View file

@ -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 = [

View file

@ -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="")

View file

@ -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>

View file

@ -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>

View file

@ -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
)

View file

@ -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.

View file

@ -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

View file

@ -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 {

File diff suppressed because one or more lines are too long

View file

@ -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());
}
});
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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
}

View file

@ -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

View file

@ -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),

View file

@ -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>

View file

@ -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">

View file

@ -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
View file

11
music/admin.py Normal file
View 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
View file

@ -0,0 +1,5 @@
from django.apps import AppConfig
class MusicConfig(AppConfig):
name = 'music'

178
music/forms.py Normal file
View 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
View 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")
]

View 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>

View 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">&laquo;</a>
<a href="?page={{ marks.previous_page_number }}"
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">&lsaquo;</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">&rsaquo;</a>
<a href="?page={{ marks.pagination.last_page }}"
class="pagination__nav-link">&raquo;</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>

View 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>

View 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">&laquo;</a>
<a href="?page={{ reviews.previous_page_number }}"
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">&lsaquo;</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">&rsaquo;</a>
<a href="?page={{ reviews.pagination.last_page }}"
class="pagination__nav-link">&raquo;</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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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">&laquo;</a>
<a href="?page={{ marks.previous_page_number }}"
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">&lsaquo;</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">&rsaquo;</a>
<a href="?page={{ marks.pagination.last_page }}"
class="pagination__nav-link">&raquo;</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>

View 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>

View 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">&laquo;</a>
<a href="?page={{ reviews.previous_page_number }}"
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">&lsaquo;</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">&rsaquo;</a>
<a href="?page={{ reviews.pagination.last_page }}" class="pagination__nav-link">&raquo;</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
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

40
music/urls.py Normal file
View 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

File diff suppressed because it is too large Load diff

View file

@ -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'],