Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
B
Backend Server
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
SpinShare
Backend Server
Commits
c219a3d5
Commit
c219a3d5
authored
Jan 25, 2021
by
SeeBeyond
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'difficultyrating-songfilters' into 'master'
Difficultyrating songfilters See merge request
!2
parents
0a06774a
fb0084e8
Changes
32
Show whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
1327 additions
and
244 deletions
+1327
-244
public/assets/css/main.css
public/assets/css/main.css
+5
-5
public/assets/css/main.less
public/assets/css/main.less
+5
-5
public/assets/css/search.css
public/assets/css/search.css
+107
-27
public/assets/css/search.less
public/assets/css/search.less
+135
-44
public/assets/css/songdetail.css
public/assets/css/songdetail.css
+11
-7
public/assets/css/songdetail.less
public/assets/css/songdetail.less
+14
-8
src/Controller/API/APIDiscoveryController.php
src/Controller/API/APIDiscoveryController.php
+172
-21
src/Controller/IndexController.php
src/Controller/IndexController.php
+41
-7
src/Controller/Moderation/ClientReleasesController.php
src/Controller/Moderation/ClientReleasesController.php
+2
-2
src/Controller/Moderation/SystemController.php
src/Controller/Moderation/SystemController.php
+157
-2
src/Controller/SearchController.php
src/Controller/SearchController.php
+76
-13
src/Controller/SongController.php
src/Controller/SongController.php
+33
-0
src/Controller/UploadController.php
src/Controller/UploadController.php
+27
-0
src/Entity/Song.php
src/Entity/Song.php
+146
-0
src/Entity/User.php
src/Entity/User.php
+1
-1
src/Repository/SongRepository.php
src/Repository/SongRepository.php
+31
-5
templates/apidocs/gettingstarted/introduction.html.twig
templates/apidocs/gettingstarted/introduction.html.twig
+14
-9
templates/apidocs/open/discovery.html.twig
templates/apidocs/open/discovery.html.twig
+140
-15
templates/apidocs/open/playlists.html.twig
templates/apidocs/open/playlists.html.twig
+17
-4
templates/apidocs/open/songs.html.twig
templates/apidocs/open/songs.html.twig
+46
-38
templates/base.html.twig
templates/base.html.twig
+7
-0
templates/components/song-item.html.twig
templates/components/song-item.html.twig
+5
-10
templates/index/base.html.twig
templates/index/base.html.twig
+3
-1
templates/index/hotThisMonth.html.twig
templates/index/hotThisMonth.html.twig
+23
-0
templates/index/hotThisWeek.html.twig
templates/index/hotThisWeek.html.twig
+5
-5
templates/index/support.html.twig
templates/index/support.html.twig
+1
-1
templates/index/updated.html.twig
templates/index/updated.html.twig
+23
-0
templates/moderation/migration/difficultyRating.html.twig
templates/moderation/migration/difficultyRating.html.twig
+4
-0
templates/moderation/migration/updateHash.html.twig
templates/moderation/migration/updateHash.html.twig
+4
-0
templates/moderation/system/index.html.twig
templates/moderation/system/index.html.twig
+5
-2
templates/search/index.html.twig
templates/search/index.html.twig
+52
-7
templates/song/detail.html.twig
templates/song/detail.html.twig
+15
-5
No files found.
public/assets/css/main.css
View file @
c219a3d5
...
...
@@ -364,6 +364,11 @@ input[type="range"]::-ms-fill-lower {
grid-template-columns
:
repeat
(
6
,
1
fr
);
grid-gap
:
15px
;
}
.song-row
.song-list-5
{
display
:
grid
;
grid-template-columns
:
repeat
(
5
,
1
fr
);
grid-gap
:
15px
;
}
.song-row
.pagination
{
text-align
:
right
;
margin-top
:
25px
;
...
...
@@ -470,14 +475,10 @@ input[type="range"]::-ms-fill-lower {
padding
:
3px
8px
;
margin-right
:
4px
;
font-size
:
10px
;
opacity
:
0.3
;
}
.song-item
.song-metadata
.song-difficulties
.difficulty
span
{
font-weight
:
bold
;
}
.song-item
.song-metadata
.song-difficulties
.difficulty.active
{
opacity
:
1
;
}
.song-item
:not
(
.inactive
)
:hover
{
cursor
:
pointer
;
background
:
rgba
(
255
,
255
,
255
,
0.2
);
...
...
@@ -630,4 +631,3 @@ input[type="range"]::-ms-fill-lower {
grid-template-columns
:
repeat
(
6
,
1
fr
);
}
}
/*# sourceMappingURL=main.css.map */
\ No newline at end of file
public/assets/css/main.less
View file @
c219a3d5
...
...
@@ -379,6 +379,11 @@ input[type="range"]::-ms-fill-lower {
grid-template-columns: repeat(6, 1fr);
grid-gap: 15px;
}
& .song-list-5 {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-gap: 15px;
}
& .pagination {
text-align: right;
margin-top: 25px;
...
...
@@ -494,16 +499,11 @@ input[type="range"]::-ms-fill-lower {
padding: 3px 8px;
margin-right: 4px;
font-size: 10px;
opacity: 0.3;
& span {
// padding-right: 3px;
font-weight: bold;
}
&.active {
opacity: 1;
}
}
}
}
...
...
public/assets/css/search.css
View file @
c219a3d5
.section-search
{
display
:
grid
;
grid-template-rows
:
auto
1
fr
;
grid-gap
:
25px
;
min-height
:
100vh
;
grid-template-rows
:
1
fr
;
grid-template-columns
:
350px
1
fr
;
grid-template-areas
:
"filters results"
;
}
.section-search
header
{
background
:
rgba
(
0
,
0
,
0
,
0.5
)
;
padding
:
50px
;
padding
-bottom
:
25
px
;
.section-search
.search-filters
{
grid-area
:
filters
;
background
:
#101314
;
padding
:
30
px
;
}
.section-search
header
.title
{
font-size
:
32px
;
letter-spacing
:
0.05em
;
.section-search
.search-filters
header
{
font-size
:
14px
;
letter-spacing
:
0.25em
;
font-weight
:
bold
;
text-transform
:
uppercase
;
margin-bottom
:
15px
;
font-family
:
'Oswald'
,
sans-serif
;
}
.section-search
header
.actions
{
.section-search
.search-filters
.filter-input
{
margin-bottom
:
25px
;
}
.section-search
.search-filters
.filter-input
.filter-difficulty
{
position
:
relative
;
display
:
grid
;
grid-template-columns
:
1
fr
auto
;
grid-gap
:
15px
;
grid-template-columns
:
auto
1
fr
;
grid-gap
:
10px
;
margin-top
:
10px
;
cursor
:
pointer
;
align-items
:
center
;
overflow
:
hidden
;
}
.section-search
.search-filters
.filter-input
.filter-difficulty
input
{
position
:
absolute
;
top
:
0px
;
left
:
0px
;
bottom
:
0px
;
right
:
0px
;
visibility
:
hidden
;
}
.section-search
.search-filters
.filter-input
.filter-difficulty
input
:checked
+
span
{
background
:
rgba
(
123
,
228
,
163
,
0.3
);
}
.section-search
.search-filters
.filter-input
.filter-difficulty
input
:checked
+
span
::before
{
transform
:
translateX
(
20px
);
background
:
#7be4a3
;
}
.section-search
.search-filters
.filter-input
.filter-difficulty
span
{
height
:
20px
;
width
:
40px
;
background
:
rgba
(
255
,
255
,
255
,
0.2
);
border-radius
:
100px
;
transition
:
0.2s
ease-in-out
all
;
}
.section-search
header
.actions
.show-all
{
height
:
100%
;
display
:
flex
;
justify-content
:
center
;
.section-search
.search-filters
.filter-input
.filter-difficulty
span
::before
{
transition
:
0.2s
ease-in-out
all
;
content
:
""
;
display
:
block
;
height
:
20px
;
width
:
20px
;
border-radius
:
100px
;
background
:
#fff
;
}
.section-search
.search-filters
.filter-input
.filter-difficulty
div
{
display
:
block
;
position
:
relative
;
}
.section-search
.search-filters
.filter-input
.filter-rating
{
position
:
relative
;
display
:
grid
;
grid-template-columns
:
auto
1
fr
;
grid-gap
:
10px
;
margin-top
:
10px
;
cursor
:
pointer
;
align-items
:
center
;
padding
:
0px
10px
;
}
.section-search
header
.actions
input
{
width
:
100%
;
.section-search
.search-filters
.filter-input
.filter-rating
div
{
display
:
block
;
position
:
relative
;
}
.section-search
.search-filters
.filter-input
.filter-rating
input
[
type
=
"number"
]
{
width
:
75px
;
font-family
:
'Open Sans'
,
sans-serif
;
font-size
:
14px
;
background
:
transparent
;
color
:
#fff
;
border-radius
:
4px
;
padding
:
9px
20px
;
...
...
@@ -40,22 +88,54 @@
border
:
0px
;
transition
:
0.2s
ease-in-out
all
;
}
.section-search
header
.actions
input
:hover
{
.section-search
.search-filters
.filter-input
.filter-rating
input
[
type
=
"number"
]
:hover
{
background
:
rgba
(
255
,
255
,
255
,
0.3
);
color
:
#fff
;
}
.section-search
header
.actions
input
:focus
{
.section-search
.search-filters
.filter-input
.filter-rating
input
[
type
=
"number"
]
:focus
{
outline
:
0
;
background
:
rgba
(
255
,
255
,
255
,
0.3
);
}
.section-search
header
.actions
input
::placeholder
{
color
:
rgba
(
255
,
255
,
255
,
0.6
);
.section-search
.search-filters
.filter-input
.filter-rating
input
[
type
=
"number"
]
::placeholder
{
color
:
rgba
(
255
,
255
,
255
,
0.5
);
}
.section-search
.search-filters
input
[
type
=
"submit"
]
{
font-family
:
'Open Sans'
,
sans-serif
;
text-decoration
:
none
;
border
:
0px
;
color
:
#fff
;
font-size
:
12px
;
font-weight
:
bold
;
letter-spacing
:
0.1em
;
background
:
linear-gradient
(
135deg
,
rgba
(
255
,
255
,
255
,
0.3
),
rgba
(
255
,
255
,
255
,
0.1
));
padding
:
10px
20px
;
border-radius
:
4px
;
text-transform
:
uppercase
;
transition
:
0.2s
ease-in-out
all
;
cursor
:
pointer
;
}
.section-search
.search-filters
input
[
type
=
"submit"
]
:hover
{
opacity
:
0.6
;
text-decoration
:
none
;
}
.section-search
.search-filters
input
[
type
=
"submit"
]
:focus
{
outline
:
0
;
}
.section-search
.search-filters
input
[
type
=
"submit"
]
:disabled
,
.section-search
.search-filters
input
[
type
=
"submit"
]
.button-disabled
{
opacity
:
0.4
;
cursor
:
not-allowed
;
}
.section-search
.search-filters
input
[
type
=
"submit"
]
.button-icon
{
padding
:
5px
15px
;
font-size
:
18px
;
}
.section-search
.search-results
{
display
:
grid
;
grid-area
:
results
;
grid-template-rows
:
auto
auto
auto
1
fr
;
grid-gap
:
25px
;
padding
:
0px
5
0px
;
padding
:
3
0px
;
}
.section-search
.search-results
.search-results-users
{
display
:
grid
;
...
...
public/assets/css/search.less
View file @
c219a3d5
.section-search {
display: grid;
grid-template-rows: auto 1fr;
grid-gap: 25px;
min-height: 100vh;
grid-template-rows: 1fr;
grid-template-columns: 350px 1fr;
grid-template-areas: "filters results";
&
header
{
background: rgba(0,0,0,0.5)
;
padding: 50px
;
padding
-bottom: 25
px;
&
.search-filters
{
grid-area: filters
;
background: #101314
;
padding
: 30
px;
& .title {
font-size: 32px;
letter-spacing: 0.05em;
& header {
font-size: 14px;
letter-spacing: 0.25em;
font-weight: bold;
text-transform: uppercase;
margin-bottom: 15px;
font-family: 'Oswald', sans-serif;
}
& .actions {
& .filter-input {
margin-bottom: 25px;
& .filter-difficulty {
position: relative;
display: grid;
grid-template-columns: 1fr auto;
grid-gap: 15px;
grid-template-columns: auto 1fr;
grid-gap: 10px;
margin-top: 10px;
cursor: pointer;
align-items: center;
overflow: hidden;
& input {
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
visibility: hidden;
&:checked + span {
background: rgba(123, 228, 163, 0.3);
}
&:checked + span::before {
transform: translateX(20px);
background: rgb(123, 228, 163);
}
}
& span {
height: 20px;
width: 40px;
background: rgba(255,255,255,0.2);
border-radius: 100px;
transition: 0.2s ease-in-out all;
& .show-all {
height: 100%;
display: flex;
justify-content: center;
&::before {
transition: 0.2s ease-in-out all;
content: "";
display: block;
height: 20px;
width: 20px;
border-radius: 100px;
background: #fff;
}
}
& div {
display: block;
position: relative;
}
}
& .filter-rating {
position: relative;
display: grid;
grid-template-columns: auto 1fr;
grid-gap: 10px;
margin-top: 10px;
cursor: pointer;
align-items: center;
padding: 0px 10px;
& div {
display: block;
position: relative;
}
input
{
width: 100%
;
& input[type="number"]
{
width: 75px
;
font-family: 'Open Sans', sans-serif;
font-size: 14px;
background: transparent;
color: #fff;
border-radius: 4px;
padding: 9px 20px;
...
...
@@ -50,17 +105,53 @@
background: rgba(255,255,255,0.3);
}
&::placeholder {
color: rgba(255,255,255,0.6);
color: rgba(255,255,255,0.5);
}
}
}
}
& input[type="submit"] {
font-family: 'Open Sans', sans-serif;
font-size: 12px;
text-decoration: none;
border: 0px;
color: #fff;
font-size: 12px;
font-weight: bold;
letter-spacing: 0.1em;
background: linear-gradient(135deg, rgba(255,255,255,0.3), rgba(255,255,255,0.1));
padding: 10px 20px;
border-radius: 4px;
text-transform: uppercase;
transition: 0.2s ease-in-out all;
cursor: pointer;
&:hover {
opacity: 0.6;
text-decoration: none;
}
&:focus {
outline: 0;
}
&:disabled, &.button-disabled {
opacity: 0.4;
cursor: not-allowed;
}
&.button-icon {
padding: 5px 15px;
font-size: 18px;
}
}
}
& .search-results {
display: grid;
grid-area: results;
grid-template-rows: auto auto auto 1fr;
grid-gap: 25px;
padding:
0px 5
0px;
padding:
3
0px;
& .search-results-users {
display: grid;
...
...
public/assets/css/songdetail.css
View file @
c219a3d5
...
...
@@ -125,14 +125,19 @@
}
.section-song-detail
.song-detail
.song-statistics
.stat
.difficulties
{
align-self
:
center
;
height
:
20px
;
display
:
flex
;
}
.section-song-detail
.song-detail
.song-statistics
.stat
.difficulties
img
{
height
:
25px
;
margin-right
:
5px
;
opacity
:
0.4
;
.section-song-detail
.song-detail
.song-statistics
.stat
.difficulties
.difficulty
{
background
:
#fff
;
color
:
#000
;
border-radius
:
4px
;
padding
:
3px
8px
;
margin-right
:
4px
;
font-size
:
10px
;
}
.section-song-detail
.song-detail
.song-statistics
.stat
.difficulties
img
.active
{
opacity
:
1
;
.section-song-detail
.song-detail
.song-statistics
.stat
.difficulties
.difficulty
span
{
font-weight
:
bold
;
}
.section-song-detail
.song-detail
.song-statistics
.stat
.content
{
align-self
:
center
;
...
...
@@ -687,4 +692,3 @@
grid-template-columns
:
1
fr
;
}
}
/*# sourceMappingURL=songdetail.css.map */
\ No newline at end of file
public/assets/css/songdetail.less
View file @
c219a3d5
...
...
@@ -143,14 +143,20 @@
& .difficulties {
align-self: center;
height: 20px;
display: flex;
& img {
height: 25px;
margin-right: 5px;
opacity: 0.4;
& .difficulty {
background: #fff;
color: #000;
border-radius: 4px;
padding: 3px 8px;
margin-right: 4px;
font-size: 10px;
&.active {
opacity: 1;
& span {
// padding-right: 3px;
font-weight: bold;
}
}
}
...
...
src/Controller/API/APIDiscoveryController.php
View file @
c219a3d5
...
...
@@ -44,6 +44,13 @@ class APIDiscoveryController extends AbstractController
$oneResult
[
'hasHardDifficulty'
]
=
$result
->
getHasHardDifficulty
();
$oneResult
[
'hasExtremeDifficulty'
]
=
$result
->
getHasExtremeDifficulty
();
$oneResult
[
'hasXDDifficulty'
]
=
$result
->
getHasXDDifficulty
();
$oneResult
[
'easyDifficulty'
]
=
$result
->
getEasyDifficulty
();
$oneResult
[
'normalDifficulty'
]
=
$result
->
getNormalDifficulty
();
$oneResult
[
'hardDifficulty'
]
=
$result
->
getHardDifficulty
();
$oneResult
[
'expertDifficulty'
]
=
$result
->
getExpertDifficulty
();
$oneResult
[
'XDDifficulty'
]
=
$result
->
getXDDifficulty
();
$oneResult
[
'updateDate'
]
=
$result
->
getUpdateDate
();
$oneResult
[
'updateHash'
]
=
$result
->
getUpdateHash
();
$oneResult
[
'cover'
]
=
$baseUrl
.
"/uploads/thumbnail/"
.
$result
->
getFileReference
()
.
".jpg"
;
$oneResult
[
'zip'
]
=
$this
->
generateUrl
(
'api.songs.download'
,
array
(
'id'
=>
$result
->
getId
()),
UrlGeneratorInterface
::
ABSOLUTE_URL
);
...
...
@@ -55,15 +62,15 @@ class APIDiscoveryController extends AbstractController
}
/**
* @Route("/api/songs/
hot/{offset}", name="api.songs.hot
")
* @Route("/api/songs/
hot
/{offset}/")
* @Route("/api/songs/
updated/{offset}", name="api.songs.updated
")
* @Route("/api/songs/
updated
/{offset}/")
*/
public
function
songs
Hot
(
Request
$request
,
int
$offset
=
0
)
public
function
songs
Updated
(
Request
$request
,
int
$offset
=
0
)
{
$em
=
$this
->
getDoctrine
()
->
getManager
();
$data
=
[];
$results
=
$em
->
getRepository
(
Song
::
class
)
->
get
Hot
(
$offset
);
$results
=
$em
->
getRepository
(
Song
::
class
)
->
get
Updated
(
$offset
);
$baseUrl
=
$request
->
getScheme
()
.
'://'
.
$request
->
getHttpHost
()
.
$request
->
getBasePath
();
foreach
(
$results
as
$result
)
{
...
...
@@ -79,6 +86,13 @@ class APIDiscoveryController extends AbstractController
$oneResult
[
'hasHardDifficulty'
]
=
$result
->
getHasHardDifficulty
();
$oneResult
[
'hasExtremeDifficulty'
]
=
$result
->
getHasExtremeDifficulty
();
$oneResult
[
'hasXDDifficulty'
]
=
$result
->
getHasXDDifficulty
();
$oneResult
[
'easyDifficulty'
]
=
$result
->
getEasyDifficulty
();
$oneResult
[
'normalDifficulty'
]
=
$result
->
getNormalDifficulty
();
$oneResult
[
'hardDifficulty'
]
=
$result
->
getHardDifficulty
();
$oneResult
[
'expertDifficulty'
]
=
$result
->
getExpertDifficulty
();
$oneResult
[
'XDDifficulty'
]
=
$result
->
getXDDifficulty
();
$oneResult
[
'updateDate'
]
=
$result
->
getUpdateDate
();
$oneResult
[
'updateHash'
]
=
$result
->
getUpdateHash
();
$oneResult
[
'cover'
]
=
$baseUrl
.
"/uploads/thumbnail/"
.
$result
->
getFileReference
()
.
".jpg"
;
$oneResult
[
'zip'
]
=
$this
->
generateUrl
(
'api.songs.download'
,
array
(
'id'
=>
$result
->
getId
()),
UrlGeneratorInterface
::
ABSOLUTE_URL
);
...
...
@@ -90,16 +104,86 @@ class APIDiscoveryController extends AbstractController
}
/**
* @Route("/api/songs/
popular/{offset}", name="api.songs.popular
")
* @Route("/api/songs/
popular
/{offset}/")
* @Route("/api/songs/
hotThisWeek/{offset}", name="api.songs.hotThisWeek
")
* @Route("/api/songs/
hotThisWeek
/{offset}/")
*/
public
function
songs
Popular
(
Request
$request
,
int
$offset
=
0
)
public
function
songs
HotThisWeek
(
Request
$request
,
int
$offset
=
0
)
{
// TODO: Remove this later on
$em
=
$this
->
getDoctrine
()
->
getManager
();
$data
=
[];
$response
=
new
JsonResponse
([
'version'
=>
$this
->
getParameter
(
'api_version'
),
'status'
=>
200
,
'data'
=>
[]]);
$results
=
$em
->
getRepository
(
Song
::
class
)
->
getHotThisWeek
(
$offset
);
$baseUrl
=
$request
->
getScheme
()
.
'://'
.
$request
->
getHttpHost
()
.
$request
->
getBasePath
();
foreach
(
$results
as
$result
)
{
$oneResult
=
[];
$oneResult
[
'id'
]
=
$result
->
getId
();
$oneResult
[
'title'
]
=
$result
->
getTitle
();
$oneResult
[
'subtitle'
]
=
$result
->
getSubtitle
();
$oneResult
[
'artist'
]
=
$result
->
getArtist
();
$oneResult
[
'charter'
]
=
$result
->
getCharter
();
$oneResult
[
'hasEasyDifficulty'
]
=
$result
->
getHasEasyDifficulty
();
$oneResult
[
'hasNormalDifficulty'
]
=
$result
->
getHasNormalDifficulty
();
$oneResult
[
'hasHardDifficulty'
]
=
$result
->
getHasHardDifficulty
();
$oneResult
[
'hasExtremeDifficulty'
]
=
$result
->
getHasExtremeDifficulty
();
$oneResult
[
'hasXDDifficulty'
]
=
$result
->
getHasXDDifficulty
();
$oneResult
[
'easyDifficulty'
]
=
$result
->
getEasyDifficulty
();
$oneResult
[
'normalDifficulty'
]
=
$result
->
getNormalDifficulty
();
$oneResult
[
'hardDifficulty'
]
=
$result
->
getHardDifficulty
();
$oneResult
[
'expertDifficulty'
]
=
$result
->
getExpertDifficulty
();
$oneResult
[
'XDDifficulty'
]
=
$result
->
getXDDifficulty
();
$oneResult
[
'updateDate'
]
=
$result
->
getUpdateDate
();
$oneResult
[
'updateHash'
]
=
$result
->
getUpdateHash
();
$oneResult
[
'cover'
]
=
$baseUrl
.
"/uploads/thumbnail/"
.
$result
->
getFileReference
()
.
".jpg"
;
$oneResult
[
'zip'
]
=
$this
->
generateUrl
(
'api.songs.download'
,
array
(
'id'
=>
$result
->
getId
()),
UrlGeneratorInterface
::
ABSOLUTE_URL
);
$data
[]
=
$oneResult
;
}
$response
=
new
JsonResponse
([
'version'
=>
$this
->
getParameter
(
'api_version'
),
'status'
=>
200
,
'data'
=>
$data
]);
return
$response
;
}
/**
* @Route("/api/songs/hotThisMonth/{offset}", name="api.songs.hotThisMonth")
* @Route("/api/songs/hotThisMonth/{offset}/")
*/
public
function
songsHotThisMonth
(
Request
$request
,
int
$offset
=
0
)
{
$em
=
$this
->
getDoctrine
()
->
getManager
();
$data
=
[];
$results
=
$em
->
getRepository
(
Song
::
class
)
->
getHotThisMonth
(
$offset
);
$baseUrl
=
$request
->
getScheme
()
.
'://'
.
$request
->
getHttpHost
()
.
$request
->
getBasePath
();
foreach
(
$results
as
$result
)
{
$oneResult
=
[];
$oneResult
[
'id'
]
=
$result
->
getId
();
$oneResult
[
'title'
]
=
$result
->
getTitle
();
$oneResult
[
'subtitle'
]
=
$result
->
getSubtitle
();
$oneResult
[
'artist'
]
=
$result
->
getArtist
();
$oneResult
[
'charter'
]
=
$result
->
getCharter
();
$oneResult
[
'hasEasyDifficulty'
]
=
$result
->
getHasEasyDifficulty
();
$oneResult
[
'hasNormalDifficulty'
]
=
$result
->
getHasNormalDifficulty
();
$oneResult
[
'hasHardDifficulty'
]
=
$result
->
getHasHardDifficulty
();
$oneResult
[
'hasExtremeDifficulty'
]
=
$result
->
getHasExtremeDifficulty
();
$oneResult
[
'hasXDDifficulty'
]
=
$result
->
getHasXDDifficulty
();
$oneResult
[
'easyDifficulty'
]
=
$result
->
getEasyDifficulty
();
$oneResult
[
'normalDifficulty'
]
=
$result
->
getNormalDifficulty
();
$oneResult
[
'hardDifficulty'
]
=
$result
->
getHardDifficulty
();
$oneResult
[
'expertDifficulty'
]
=
$result
->
getExpertDifficulty
();
$oneResult
[
'XDDifficulty'
]
=
$result
->
getXDDifficulty
();
$oneResult
[
'updateDate'
]
=
$result
->
getUpdateDate
();
$oneResult
[
'updateHash'
]
=
$result
->
getUpdateHash
();
$oneResult
[
'cover'
]
=
$baseUrl
.
"/uploads/thumbnail/"
.
$result
->
getFileReference
()
.
".jpg"
;
$oneResult
[
'zip'
]
=
$this
->
generateUrl
(
'api.songs.download'
,
array
(
'id'
=>
$result
->
getId
()),
UrlGeneratorInterface
::
ABSOLUTE_URL
);
$data
[]
=
$oneResult
;
}
$response
=
new
JsonResponse
([
'version'
=>
$this
->
getParameter
(
'api_version'
),
'status'
=>
200
,
'data'
=>
$data
]);
return
$response
;
}
...
...
@@ -166,6 +250,13 @@ class APIDiscoveryController extends AbstractController
$oneResult
[
'hasHardDifficulty'
]
=
$result
->
getHasHardDifficulty
();
$oneResult
[
'hasExtremeDifficulty'
]
=
$result
->
getHasExtremeDifficulty
();
$oneResult
[
'hasXDDifficulty'
]
=
$result
->
getHasXDDifficulty
();
$oneResult
[
'easyDifficulty'
]
=
$result
->
getEasyDifficulty
();
$oneResult
[
'normalDifficulty'
]
=
$result
->
getNormalDifficulty
();
$oneResult
[
'hardDifficulty'
]
=
$result
->
getHardDifficulty
();
$oneResult
[
'expertDifficulty'
]
=
$result
->
getExpertDifficulty
();
$oneResult
[
'XDDifficulty'
]
=
$result
->
getXDDifficulty
();
$oneResult
[
'updateDate'
]
=
$result
->
getUpdateDate
();
$oneResult
[
'updateHash'
]
=
$result
->
getUpdateHash
();
$oneResult
[
'cover'
]
=
$baseUrl
.
"/uploads/thumbnail/"
.
$result
->
getFileReference
()
.
".jpg"
;
$oneResult
[
'zip'
]
=
$this
->
generateUrl
(
'api.songs.download'
,
array
(
'id'
=>
$result
->
getId
()),
UrlGeneratorInterface
::
ABSOLUTE_URL
);
...
...
@@ -223,19 +314,65 @@ class APIDiscoveryController extends AbstractController
}
// Songs
$resultsSongs
=
$em
->
getRepository
(
Song
::
class
)
->
createQueryBuilder
(
'o'
)
->
where
(
'o.title LIKE :query'
)
->
orWhere
(
'o.subtitle LIKE :query'
)
->
orWhere
(
'o.tags LIKE :query'
)
->
orWhere
(
'o.artist LIKE :query'
)
->
orWhere
(
'o.charter LIKE :query'
)
->
andWhere
(
'o.publicationStatus IN (0, 1)'
)
->
orderBy
(
'o.id'
,
'DESC'
)
->
setParameter
(
'query'
,
'%'
.
$searchQuery
.
'%'
)
->
getQuery
()
->
getResult
();
$resultsSongs
=
$em
->
getRepository
(
Song
::
class
)
->
createQueryBuilder
(
'o'
);
$resultsSongs
->
where
(
'o.title LIKE :query'
);
$resultsSongs
->
orWhere
(
'o.subtitle LIKE :query'
);
$resultsSongs
->
orWhere
(
'o.tags LIKE :query'
);
$resultsSongs
->
orWhere
(
'o.artist LIKE :query'
);
$resultsSongs
->
orWhere
(
'o.charter LIKE :query'
);
// Add Filters for difficulty
$filterEasy
=
isset
(
$jsonBody
[
'diffEasy'
])
?
$jsonBody
[
'diffEasy'
]
:
true
;
$filterNormal
=
isset
(
$jsonBody
[
'diffNormal'
])
?
$jsonBody
[
'diffNormal'
]
:
true
;
$filterHard
=
isset
(
$jsonBody
[
'diffHard'
])
?
$jsonBody
[
'diffHard'
]
:
true
;
$filterExpert
=
isset
(
$jsonBody
[
'diffExpert'
])
?
$jsonBody
[
'diffExpert'
]
:
true
;
$filterXD
=
isset
(
$jsonBody
[
'diffXD'
])
?
$jsonBody
[
'diffXD'
]
:
true
;
// Add Filters for difficulty ratings
$filterMinDifficulty
=
intval
(
isset
(
$jsonBody
[
'diffRatingFrom'
])
?
$jsonBody
[
'diffRatingFrom'
]
:
0
);
$filterMaxDifficulty
=
intval
(
isset
(
$jsonBody
[
'diffRatingTo'
])
?
$jsonBody
[
'diffRatingTo'
]
:
99
);
if
(
$filterMinDifficulty
==
null
)
{
$filterMinDifficulty
=
0
;
}
if
(
$filterMaxDifficulty
==
null
)
{
$filterMaxDifficulty
=
99
;
}
$resultsSongs
->
setParameter
(
'query'
,
"%"
.
$searchQuery
.
"%"
);
$resultsSongs
->
andWhere
(
'o.publicationStatus IN (0, 1)'
);
$resultsSongs
->
orderBy
(
'o.id'
,
'DESC'
);
$resultsSongs
=
$resultsSongs
->
getQuery
()
->
getResult
();
// Filter Song Results
$filteredResultsSongs
=
[];
foreach
(
$resultsSongs
as
$resultSong
)
{
// Has the required difficulty
if
(
$filterEasy
&&
$resultSong
->
getHasEasyDifficulty
()
||
$filterNormal
&&
$resultSong
->
getHasNormalDifficulty
()
||
$filterHard
&&
$resultSong
->
getHasHardDifficulty
()
||
$filterExpert
&&
$resultSong
->
getHasExtremeDifficulty
()
||
$filterXD
&&
$resultSong
->
getHasXDDifficulty
())
{
// Has the minimum difficulty rating
if
(
$resultSong
->
getHasEasyDifficulty
()
&&
$resultSong
->
getEasyDifficulty
()
>=
$filterMinDifficulty
||
$resultSong
->
getHasNormalDifficulty
()
&&
$resultSong
->
getNormalDifficulty
()
>=
$filterMinDifficulty
||
$resultSong
->
getHasHardDifficulty
()
&&
$resultSong
->
getHardDifficulty
()
>=
$filterMinDifficulty
||
$resultSong
->
getHasExtremeDifficulty
()
&&
$resultSong
->
getExpertDifficulty
()
>=
$filterMinDifficulty
||
$resultSong
->
getHasXDDifficulty
()
&&
$resultSong
->
getXDDifficulty
()
>=
$filterMinDifficulty
)
{
// Has the maximum difficulty rating
if
(
$resultSong
->
getHasEasyDifficulty
()
&&
$resultSong
->
getEasyDifficulty
()
<=
$filterMaxDifficulty
||
$resultSong
->
getHasNormalDifficulty
()
&&
$resultSong
->
getNormalDifficulty
()
<=
$filterMaxDifficulty
||
$resultSong
->
getHasHardDifficulty
()
&&
$resultSong
->
getHardDifficulty
()
<=
$filterMaxDifficulty
||
$resultSong
->
getHasExtremeDifficulty
()
&&
$resultSong
->
getExpertDifficulty
()
<=
$filterMaxDifficulty
||
$resultSong
->
getHasXDDifficulty
()
&&
$resultSong
->
getXDDifficulty
()
<=
$filterMaxDifficulty
)
{
$filteredResultsSongs
[]
=
$resultSong
;
}
}
}
}
foreach
(
$
r
esultsSongs
as
$result
)
{
foreach
(
$
filteredR
esultsSongs
as
$result
)
{
$oneResult
=
[];
$oneResult
[
'id'
]
=
$result
->
getId
();
...
...
@@ -248,6 +385,13 @@ class APIDiscoveryController extends AbstractController
$oneResult
[
'hasHardDifficulty'
]
=
$result
->
getHasHardDifficulty
();
$oneResult
[
'hasExtremeDifficulty'
]
=
$result
->
getHasExtremeDifficulty
();
$oneResult
[
'hasXDDifficulty'
]
=
$result
->
getHasXDDifficulty
();
$oneResult
[
'easyDifficulty'
]
=
$result
->
getEasyDifficulty
();
$oneResult
[
'normalDifficulty'
]
=
$result
->
getNormalDifficulty
();
$oneResult
[
'hardDifficulty'
]
=
$result
->
getHardDifficulty
();
$oneResult
[
'expertDifficulty'
]
=
$result
->
getExpertDifficulty
();
$oneResult
[
'XDDifficulty'
]
=
$result
->
getXDDifficulty
();
$oneResult
[
'updateDate'
]
=
$result
->
getUpdateDate
();
$oneResult
[
'updateHash'
]
=
$result
->
getUpdateHash
();
$oneResult
[
'cover'
]
=
$baseUrl
.
"/uploads/thumbnail/"
.
$result
->
getFileReference
()
.
".jpg"
;
$oneResult
[
'zip'
]
=
$this
->
generateUrl
(
'api.songs.download'
,
array
(
'id'
=>
$result
->
getId
()),
UrlGeneratorInterface
::
ABSOLUTE_URL
);
...
...
@@ -287,6 +431,13 @@ class APIDiscoveryController extends AbstractController
$oneResult
[
'hasHardDifficulty'
]
=
$result
->
getHasHardDifficulty
();
$oneResult
[
'hasExtremeDifficulty'
]
=
$result
->
getHasExtremeDifficulty
();
$oneResult
[
'hasXDDifficulty'
]
=
$result
->
getHasXDDifficulty
();
$oneResult
[
'easyDifficulty'
]
=
$result
->
getEasyDifficulty
();
$oneResult
[
'normalDifficulty'
]
=
$result
->
getNormalDifficulty
();
$oneResult
[
'hardDifficulty'
]
=
$result
->
getHardDifficulty
();
$oneResult
[
'expertDifficulty'
]
=
$result
->
getExpertDifficulty
();
$oneResult
[
'XDDifficulty'
]
=
$result
->
getXDDifficulty
();
$oneResult
[
'updateDate'
]
=
$result
->
getUpdateDate
();
$oneResult
[
'updateHash'
]
=
$result
->
getUpdateHash
();
$oneResult
[
'cover'
]
=
$baseUrl
.
"/uploads/thumbnail/"
.
$result
->
getFileReference
()
.
".jpg"
;
$oneResult
[
'zip'
]
=
$this
->
generateUrl
(
'api.songs.download'
,
array
(
'id'
=>
$result
->
getId
()),
UrlGeneratorInterface
::
ABSOLUTE_URL
);
...
...
src/Controller/IndexController.php
View file @
c219a3d5
...
...
@@ -47,20 +47,54 @@ class IndexController extends AbstractController
}
/**
* @Route("/
hot", name="index.hot
")
* @Route("/
updated", name="index.updated
")
*/
public
function
hot
(
Request
$request
)
public
function
updated
(
Request
$request
)
{
$em
=
$this
->
getDoctrine
()
->
getManager
();
$data
=
[];
$
hotOffset
=
$request
->
query
->
get
(
'hotOffset'
)
?
$request
->
query
->
get
(
'hot
Offset'
)
:
0
;
$results
HotSongs
=
$em
->
getRepository
(
Song
::
class
)
->
getHot
(
$hot
Offset
);
$
updatedOffset
=
$request
->
query
->
get
(
'updatedOffset'
)
?
$request
->
query
->
get
(
'updated
Offset'
)
:
0
;
$results
UpdatedSongs
=
$em
->
getRepository
(
Song
::
class
)
->
getUpdated
(
$updated
Offset
);
$data
[
'
hotSongs'
]
=
$resultsHot
Songs
;
$data
[
'
hotOffset'
]
=
$hot
Offset
;
$data
[
'
updatedSongs'
]
=
$resultsUpdated
Songs
;
$data
[
'
updatedOffset'
]
=
$updated
Offset
;
return
$this
->
render
(
'index/hot.html.twig'
,
$data
);
return
$this
->
render
(
'index/updated.html.twig'
,
$data
);
}
/**
* @Route("/hotThisWeek", name="index.hotThisWeek")
*/
public
function
hotThisWeek
(
Request
$request
)
{
$em
=
$this
->
getDoctrine
()
->
getManager
();
$data
=
[];
$hotWeekOffset
=
$request
->
query
->
get
(
'hotWeekOffset'
)
?
$request
->
query
->
get
(
'hotWeekOffset'
)
:
0
;
$resultsHotWeekSongs
=
$em
->
getRepository
(
Song
::
class
)
->
getHotThisWeek
(
$hotWeekOffset
);
$data
[
'hotWeekSongs'
]
=
$resultsHotWeekSongs
;
$data
[
'hotWeekOffset'
]
=
$hotWeekOffset
;
return
$this
->
render
(
'index/hotThisWeek.html.twig'
,
$data
);
}
/**
* @Route("/hotThisMonth", name="index.hotThisMonth")
*/
public
function
hotThisMonth
(
Request
$request
)
{
$em
=
$this
->
getDoctrine
()
->
getManager
();
$data
=
[];
$hotMonthOffset
=
$request
->
query
->
get
(
'hotMonthOffset'
)
?
$request
->
query
->
get
(
'hotMonthOffset'
)
:
0
;
$resultsHotMonthSongs
=
$em
->
getRepository
(
Song
::
class
)
->
getHotThisMonth
(
$hotMonthOffset
);
$data
[
'hotMonthSongs'
]
=
$resultsHotMonthSongs
;
$data
[
'hotMonthOffset'
]
=
$hotMonthOffset
;
return
$this
->
render
(
'index/hotThisMonth.html.twig'
,
$data
);
}
/**
...
...
src/Controller/Moderation/ClientReleasesController.php
View file @
c219a3d5
...
...
@@ -83,7 +83,7 @@ class ClientReleasesController extends AbstractController
$em
->
persist
(
$newRelease
);
$em
->
flush
();
return
$this
->
redirectToRoute
(
'moderation.index'
);
return
$this
->
redirectToRoute
(
'moderation.
clientreleases.
index'
);
}
catch
(
FileException
$e
)
{
}
...
...
@@ -118,6 +118,6 @@ class ClientReleasesController extends AbstractController
$em
->
remove
(
$releaseToRemove
);
$em
->
flush
();
return
$this
->
redirectToRoute
(
'moderation.index'
);
return
$this
->
redirectToRoute
(
'moderation.
clientreleases.
index'
);
}
}
src/Controller/Moderation/SystemController.php
View file @
c219a3d5
...
...
@@ -127,8 +127,163 @@ class SystemController extends AbstractController
exit
;
}
return
$this
->
redirectToRoute
(
'moderation.system.index'
);
}
/**
* @Route("/moderation/system/migration/difficultyRating/{songID}", name="moderation.system.migration.difficultyRating")
*/
public
function
systemMigrationDifficultyRating
(
Request
$request
,
int
$songID
=
1
)
{
$em
=
$this
->
getDoctrine
()
->
getManager
();
$baseUrl
=
$request
->
getScheme
()
.
'://'
.
$request
->
getHttpHost
()
.
$request
->
getBasePath
();
// Calculate Max Count
$songCount
=
$em
->
getRepository
(
Song
::
class
)
->
createQueryBuilder
(
'u'
)
->
select
(
'count(u.id)'
)
->
getQuery
()
->
getSingleScalarResult
();
if
(
$songID
>
$songCount
)
{
return
$this
->
redirectToRoute
(
'moderation.system.index'
);
}
// Find Song
$songToProcess
=
$em
->
getRepository
(
Song
::
class
)
->
findOneBy
(
array
(
'id'
=>
$songID
));
// Go To Next if not found
if
(
$songToProcess
==
null
)
{
return
$this
->
render
(
'moderation/migration/difficultyRating.html.twig'
,
array
(
'songID'
=>
$songID
+
1
));
}
// Calculate MD5 Hash
try
{
// Load SRTB file
$srtbPath
=
glob
(
$this
->
getParameter
(
'srtb_path'
)
.
DIRECTORY_SEPARATOR
.
$songToProcess
->
getFileReference
()
.
".srtb"
);
$srtbContent
=
json_decode
(
file_get_contents
(
$srtbPath
[
0
]));
$trackInfo
=
null
;
$clipInfo
=
[];
$trackData
=
[];
foreach
(
$srtbContent
->
largeStringValuesContainer
->
values
as
$valueItem
)
{
if
(
strpos
(
$valueItem
->
key
,
"TrackInfo"
)
!==
false
)
{
$trackInfo
=
json_decode
(
$valueItem
->
val
);
}
if
(
strpos
(
$valueItem
->
key
,
"ClipInfo"
)
!==
false
)
{
$i
=
str_replace
(
"SO_ClipInfo_ClipInfo_"
,
""
,
$valueItem
->
key
);
$clipInfo
[
$i
]
=
json_decode
(
$valueItem
->
val
);
}
if
(
strpos
(
$valueItem
->
key
,
"TrackData"
)
!==
false
)
{
$i
=
str_replace
(
"SO_TrackData_TrackData_"
,
""
,
$valueItem
->
key
);
$trackData
[
$i
]
=
json_decode
(
$valueItem
->
val
);
}
}
// Reset Values
$songToProcess
->
setHasEasyDifficulty
(
false
);
$songToProcess
->
setHasNormalDifficulty
(
false
);
$songToProcess
->
setHasHardDifficulty
(
false
);
$songToProcess
->
setHasExtremeDifficulty
(
false
);
$songToProcess
->
setHasXDDifficulty
(
false
);
// Detect used difficulties
foreach
(
$trackInfo
->
difficulties
as
$oneData
)
{
if
(
isset
(
$oneData
->
_active
)
&&
$oneData
->
_active
||
!
isset
(
$oneData
->
_active
))
{
switch
(
$oneData
->
_difficulty
)
{
case
2
:
$songToProcess
->
setHasEasyDifficulty
(
true
);
break
;
case
3
:
$songToProcess
->
setHasNormalDifficulty
(
true
);
break
;
case
4
:
$songToProcess
->
setHasHardDifficulty
(
true
);
break
;
case
5
:
$songToProcess
->
setHasExtremeDifficulty
(
true
);
break
;
case
6
:
$songToProcess
->
setHasXDDifficulty
(
true
);
break
;
}
}
}
// Reset difficulty ratings
$songToProcess
->
setEasyDifficulty
(
false
);
$songToProcess
->
setNormalDifficulty
(
false
);
$songToProcess
->
setHardDifficulty
(
false
);
$songToProcess
->
setExpertDifficulty
(
false
);
$songToProcess
->
setXDDifficulty
(
false
);
// Detect difficulty ratings
foreach
(
$trackData
as
$trackDataItem
)
{
switch
(
$trackDataItem
->
difficultyType
)
{
case
2
:
$songToProcess
->
setEasyDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
3
:
$songToProcess
->
setNormalDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
4
:
$songToProcess
->
setHardDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
5
:
$songToProcess
->
setExpertDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
6
:
$songToProcess
->
setXDDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
}
}
$em
->
persist
(
$songToProcess
);
$em
->
flush
();
// Go To Next
return
$this
->
render
(
'moderation/migration/difficultyRating.html.twig'
,
array
(
'songID'
=>
$songID
+
1
));
}
catch
(
\Exception
$e
)
{
var_dump
(
$e
);
exit
;
}
}
/**
* @Route("/moderation/system/migration/updateHash/{songID}", name="moderation.system.migration.updateHash")
*/
public
function
systemMigrationUpdateHash
(
Request
$request
,
int
$songID
=
1
)
{
$em
=
$this
->
getDoctrine
()
->
getManager
();
$baseUrl
=
$request
->
getScheme
()
.
'://'
.
$request
->
getHttpHost
()
.
$request
->
getBasePath
();
// Calculate Max Count
$songCount
=
$em
->
getRepository
(
Song
::
class
)
->
createQueryBuilder
(
'u'
)
->
select
(
'count(u.id)'
)
->
getQuery
()
->
getSingleScalarResult
();
if
(
$songID
>
$songCount
)
{
return
$this
->
redirectToRoute
(
'moderation.system.index'
);
}
// Find Song
$songToProcess
=
$em
->
getRepository
(
Song
::
class
)
->
findOneBy
(
array
(
'id'
=>
$songID
));
// Go To Next if not found
if
(
$songToProcess
==
null
)
{
return
$this
->
render
(
'moderation/migration/updateHash.html.twig'
,
array
(
'songID'
=>
$songID
+
1
));
}
// Calculate MD5 Hash
try
{
// Load SRTB file
$srtbPath
=
glob
(
$this
->
getParameter
(
'srtb_path'
)
.
DIRECTORY_SEPARATOR
.
$songToProcess
->
getFileReference
()
.
".srtb"
);
$srtbContentRaw
=
file_get_contents
(
$srtbPath
[
0
]);
// Save MD5 Hash
$songToProcess
->
setUpdateHash
(
md5
(
$srtbContentRaw
));
$em
->
persist
(
$songToProcess
);
$em
->
flush
();
// Go To Next
return
$this
->
render
(
'moderation/migration/updateHash.html.twig'
,
array
(
'songID'
=>
$songID
+
1
));
}
catch
(
\Exception
$e
)
{
var_dump
(
$e
);
exit
;
}
}
}
src/Controller/SearchController.php
View file @
c219a3d5
...
...
@@ -30,6 +30,15 @@ class SearchController extends AbstractController
$data
[
'results'
][
'users'
]
=
$resultsUsers
;
$data
[
'results'
][
'songs'
]
=
$resultsSongs
;
$filterEasy
=
true
;
$filterNormal
=
true
;
$filterHard
=
true
;
$filterExpert
=
true
;
$filterXD
=
true
;
$filterMinDifficulty
=
0
;
$filterMaxDifficulty
=
99
;
}
else
{
if
(
$searchQuery
!=
null
)
{
$resultsUsers
=
$em
->
getRepository
(
User
::
class
)
->
createQueryBuilder
(
'o'
)
...
...
@@ -39,24 +48,78 @@ class SearchController extends AbstractController
->
getQuery
()
->
getResult
();
$resultsSongs
=
$em
->
getRepository
(
Song
::
class
)
->
createQueryBuilder
(
'o'
);
$resultsSongs
=
$em
->
getRepository
(
Song
::
class
)
->
createQueryBuilder
(
'o'
)
->
where
(
'o.title LIKE :query'
)
->
orWhere
(
'o.subtitle LIKE :query'
)
->
orWhere
(
'o.tags LIKE :query'
)
->
orWhere
(
'o.artist LIKE :query'
)
->
orWhere
(
'o.charter LIKE :query'
)
->
andWhere
(
'o.publicationStatus IN (0, 1)'
)
->
orderBy
(
'o.id'
,
'DESC'
)
->
setParameter
(
'query'
,
'%'
.
$searchQuery
.
'%'
)
->
getQuery
()
->
getResult
();
$resultsSongs
->
where
(
'o.title LIKE :query'
);
$resultsSongs
->
orWhere
(
'o.subtitle LIKE :query'
);
$resultsSongs
->
orWhere
(
'o.tags LIKE :query'
);
$resultsSongs
->
orWhere
(
'o.artist LIKE :query'
);
$resultsSongs
->
orWhere
(
'o.charter LIKE :query'
);
// Add Filters for difficulty
$filterEasy
=
$request
->
query
->
get
(
'diffEasy'
)
==
'on'
?
true
:
false
;
$filterNormal
=
$request
->
query
->
get
(
'diffNormal'
)
==
'on'
?
true
:
false
;
$filterHard
=
$request
->
query
->
get
(
'diffHard'
)
==
'on'
?
true
:
false
;
$filterExpert
=
$request
->
query
->
get
(
'diffExpert'
)
==
'on'
?
true
:
false
;
$filterXD
=
$request
->
query
->
get
(
'diffXD'
)
==
'on'
?
true
:
false
;
// Add Filters for difficulty ratings
$filterMinDifficulty
=
intval
(
$request
->
query
->
get
(
'diffRatingFrom'
));
$filterMaxDifficulty
=
intval
(
$request
->
query
->
get
(
'diffRatingTo'
));
if
(
$filterMinDifficulty
==
null
)
{
$filterMinDifficulty
=
0
;
}
if
(
$filterMaxDifficulty
==
null
)
{
$filterMaxDifficulty
=
99
;
}
$resultsSongs
->
setParameter
(
'query'
,
"%"
.
$searchQuery
.
"%"
);
$resultsSongs
->
andWhere
(
'o.publicationStatus IN (0, 1)'
);
$resultsSongs
->
orderBy
(
'o.id'
,
'DESC'
);
$resultsSongs
=
$resultsSongs
->
getQuery
()
->
getResult
();
// Filter Song Results
$filteredResultsSongs
=
[];
foreach
(
$resultsSongs
as
$resultSong
)
{
// Has the required difficulty
if
(
$filterEasy
&&
$resultSong
->
getHasEasyDifficulty
()
||
$filterNormal
&&
$resultSong
->
getHasNormalDifficulty
()
||
$filterHard
&&
$resultSong
->
getHasHardDifficulty
()
||
$filterExpert
&&
$resultSong
->
getHasExtremeDifficulty
()
||
$filterXD
&&
$resultSong
->
getHasXDDifficulty
())
{
// Has the minimum difficulty rating
if
(
$resultSong
->
getHasEasyDifficulty
()
&&
$resultSong
->
getEasyDifficulty
()
>=
$filterMinDifficulty
||
$resultSong
->
getHasNormalDifficulty
()
&&
$resultSong
->
getNormalDifficulty
()
>=
$filterMinDifficulty
||
$resultSong
->
getHasHardDifficulty
()
&&
$resultSong
->
getHardDifficulty
()
>=
$filterMinDifficulty
||
$resultSong
->
getHasExtremeDifficulty
()
&&
$resultSong
->
getExpertDifficulty
()
>=
$filterMinDifficulty
||
$resultSong
->
getHasXDDifficulty
()
&&
$resultSong
->
getXDDifficulty
()
>=
$filterMinDifficulty
)
{
// Has the maximum difficulty rating
if
(
$resultSong
->
getHasEasyDifficulty
()
&&
$resultSong
->
getEasyDifficulty
()
<=
$filterMaxDifficulty
||
$resultSong
->
getHasNormalDifficulty
()
&&
$resultSong
->
getNormalDifficulty
()
<=
$filterMaxDifficulty
||
$resultSong
->
getHasHardDifficulty
()
&&
$resultSong
->
getHardDifficulty
()
<=
$filterMaxDifficulty
||
$resultSong
->
getHasExtremeDifficulty
()
&&
$resultSong
->
getExpertDifficulty
()
<=
$filterMaxDifficulty
||
$resultSong
->
getHasXDDifficulty
()
&&
$resultSong
->
getXDDifficulty
()
<=
$filterMaxDifficulty
)
{
$filteredResultsSongs
[]
=
$resultSong
;
}
}
}
}
$data
[
'results'
][
'users'
]
=
$resultsUsers
;
$data
[
'results'
][
'songs'
]
=
$
r
esultsSongs
;
$data
[
'results'
][
'songs'
]
=
$
filteredR
esultsSongs
;
}
}
$data
[
'searchQuery'
]
=
$searchQuery
;
$data
[
'filterEasy'
]
=
$filterEasy
;
$data
[
'filterNormal'
]
=
$filterNormal
;
$data
[
'filterHard'
]
=
$filterHard
;
$data
[
'filterExpert'
]
=
$filterExpert
;
$data
[
'filterXD'
]
=
$filterXD
;
$data
[
'diffRatingFrom'
]
=
$filterMinDifficulty
;
$data
[
'diffRatingTo'
]
=
$filterMaxDifficulty
;
return
$this
->
render
(
'search/index.html.twig'
,
$data
);
}
...
...
src/Controller/SongController.php
View file @
c219a3d5
...
...
@@ -231,6 +231,7 @@ class SongController extends AbstractController
$song
->
setTags
(
$data
[
'tags'
]);
$song
->
setDescription
(
$data
[
'description'
]);
$song
->
setIsExplicit
(
$data
[
'isExplicit'
]);
$song
->
setUpdateDate
(
new
\DateTime
(
'NOW'
));
$song
->
setPublicationStatus
(
$data
[
'publicationStatus'
]);
if
(
$backupFile
)
{
...
...
@@ -305,12 +306,23 @@ class SongController extends AbstractController
$song
->
setArtist
(
$trackInfo
->
artistName
);
$song
->
setCharter
(
$trackInfo
->
charter
);
// Generate new UpdateHash
$song
->
setUpdateHash
(
md5
(
json_encode
(
$srtbContent
)));
// Reset Difficulty Ratings
$song
->
setHasEasyDifficulty
(
false
);
$song
->
setHasNormalDifficulty
(
false
);
$song
->
setHasHardDifficulty
(
false
);
$song
->
setHasExtremeDifficulty
(
false
);
$song
->
setHasXDDifficulty
(
false
);
$song
->
setEasyDifficulty
(
null
);
$song
->
setNormalDifficulty
(
null
);
$song
->
setHardDifficulty
(
null
);
$song
->
setExpertDifficulty
(
null
);
$song
->
setXDDifficulty
(
null
);
// Detect difficulties
foreach
(
$trackInfo
->
difficulties
as
$oneData
)
{
if
(
isset
(
$oneData
->
_active
)
&&
$oneData
->
_active
||
!
isset
(
$oneData
->
_active
))
{
switch
(
$oneData
->
_difficulty
)
{
...
...
@@ -332,6 +344,27 @@ class SongController extends AbstractController
}
}
}
// Detect difficulty ratings
foreach
(
$trackData
as
$trackDataItem
)
{
switch
(
$trackDataItem
->
difficultyType
)
{
case
2
:
$song
->
setEasyDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
3
:
$song
->
setNormalDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
4
:
$song
->
setHardDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
5
:
$song
->
setExpertDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
6
:
$song
->
setXDDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
}
}
}
catch
(
Exception
$e
)
{
var_dump
(
$e
);
...
...
src/Controller/UploadController.php
View file @
c219a3d5
...
...
@@ -101,6 +101,9 @@ class UploadController extends AbstractController
$song
->
setHasExtremeDifficulty
(
false
);
$song
->
setHasXDDifficulty
(
false
);
$song
->
setUpdateHash
(
md5
(
json_encode
(
$srtbContent
)));
// Detect used difficulties
foreach
(
$trackInfo
->
difficulties
as
$oneData
)
{
if
(
isset
(
$oneData
->
_active
)
&&
$oneData
->
_active
||
!
isset
(
$oneData
->
_active
))
{
switch
(
$oneData
->
_difficulty
)
{
...
...
@@ -122,7 +125,31 @@ class UploadController extends AbstractController
}
}
}
// Detect difficulty ratings
foreach
(
$trackData
as
$trackDataItem
)
{
switch
(
$trackDataItem
->
difficultyType
)
{
case
2
:
$song
->
setEasyDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
3
:
$song
->
setNormalDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
4
:
$song
->
setHardDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
5
:
$song
->
setExpertDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
case
6
:
$song
->
setXDDifficulty
(
$trackDataItem
->
difficultyRating
);
break
;
}
}
}
catch
(
Exception
$e
)
{
var_dump
(
$e
);
exit
;
$this
->
addFlash
(
'error'
,
'Uploading failed. Please report back to our development team!'
);
// clean up temp files
...
...
src/Entity/Song.php
View file @
c219a3d5
...
...
@@ -99,11 +99,41 @@ class Song
*/
private
$hasXDDifficulty
;
/**
* @ORM\Column(type="integer", nullable=true, options={"default": 0})
*/
private
$easyDifficulty
;
/**
* @ORM\Column(type="integer", nullable=true, options={"default": 0})
*/
private
$normalDifficulty
;
/**
* @ORM\Column(type="integer", nullable=true, options={"default": 0})
*/
private
$hardDifficulty
;
/**
* @ORM\Column(type="integer", nullable=true, options={"default": 0})
*/
private
$expertDifficulty
;
/**
* @ORM\Column(type="integer", nullable=true, options={"default": 0})
*/
private
$XDDifficulty
;
/**
* @ORM\Column(type="datetime")
*/
private
$uploadDate
;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private
$updateHash
;
/**
* @ORM\Column(type="text", nullable=true)
*/
...
...
@@ -124,6 +154,11 @@ class Song
*/
private
$songPlaylists
;
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private
$updateDate
;
public
function
__construct
()
{
$this
->
reviews
=
new
ArrayCollection
();
...
...
@@ -333,6 +368,86 @@ class Song
return
$this
;
}
public
function
getEasyDifficulty
()
:
?
int
{
return
$this
->
easyDifficulty
;
}
public
function
setEasyDifficulty
(
?
int
$easyDifficulty
)
:
self
{
if
(
$easyDifficulty
!=
null
)
{
$this
->
easyDifficulty
=
max
(
0
,
min
(
99
,
$easyDifficulty
));
}
else
{
$this
->
easyDifficulty
=
$easyDifficulty
;
}
return
$this
;
}
public
function
getNormalDifficulty
()
:
?
int
{
return
$this
->
normalDifficulty
;
}
public
function
setNormalDifficulty
(
?
int
$normalDifficulty
)
:
self
{
if
(
$normalDifficulty
!=
null
)
{
$this
->
normalDifficulty
=
max
(
0
,
min
(
99
,
$normalDifficulty
));
}
else
{
$this
->
normalDifficulty
=
$normalDifficulty
;
}
return
$this
;
}
public
function
getHardDifficulty
()
:
?
int
{
return
$this
->
hardDifficulty
;
}
public
function
setHardDifficulty
(
?
int
$hardDifficulty
)
:
self
{
if
(
$hardDifficulty
!=
null
)
{
$this
->
hardDifficulty
=
max
(
0
,
min
(
99
,
$hardDifficulty
));
}
else
{
$this
->
hardDifficulty
=
$hardDifficulty
;
}
return
$this
;
}
public
function
getExpertDifficulty
()
:
?
int
{
return
$this
->
expertDifficulty
;
}
public
function
setExpertDifficulty
(
?
int
$expertDifficulty
)
:
self
{
if
(
$expertDifficulty
!=
null
)
{
$this
->
expertDifficulty
=
max
(
0
,
min
(
99
,
$expertDifficulty
));
}
else
{
$this
->
expertDifficulty
=
$expertDifficulty
;
}
return
$this
;
}
public
function
getXDDifficulty
()
:
?
int
{
return
$this
->
XDDifficulty
;
}
public
function
setXDDifficulty
(
?
int
$XDDifficulty
)
:
self
{
if
(
$XDDifficulty
!=
null
)
{
$this
->
XDDifficulty
=
max
(
0
,
min
(
99
,
$XDDifficulty
));
}
else
{
$this
->
XDDifficulty
=
$XDDifficulty
;
}
return
$this
;
}
public
function
getUploadDate
()
:
?
DateTimeInterface
{
return
$this
->
uploadDate
;
...
...
@@ -345,6 +460,18 @@ class Song
return
$this
;
}
public
function
getUpdateHash
()
:
?
string
{
return
$this
->
updateHash
;
}
public
function
setUpdateHash
(
?
string
$updateHash
)
:
self
{
$this
->
updateHash
=
$updateHash
;
return
$this
;
}
public
function
getDescription
()
:
?
string
{
return
$this
->
description
;
...
...
@@ -438,7 +565,14 @@ class Song
'hasHardDifficulty'
=>
$this
->
hasHardDifficulty
,
'hasExtremeDifficulty'
=>
$this
->
hasExtremeDifficulty
,
'hasXDDifficulty'
=>
$this
->
hasXDDifficulty
,
'easyDifficulty'
=>
$this
->
easyDifficulty
,
'normalDifficulty'
=>
$this
->
normalDifficulty
,
'hardDifficulty'
=>
$this
->
hardDifficulty
,
'expertDifficulty'
=>
$this
->
expertDifficulty
,
'XDDifficulty'
=>
$this
->
XDDifficulty
,
'uploadDate'
=>
$this
->
uploadDate
,
'updateDate'
=>
$this
->
updateDate
,
'updateHash'
=>
$this
->
updateHash
,
'description'
=>
$this
->
description
);
}
...
...
@@ -470,4 +604,16 @@ class Song
return
$this
;
}
public
function
getUpdateDate
()
:
?
\DateTimeInterface
{
return
$this
->
updateDate
;
}
public
function
setUpdateDate
(
?
\DateTimeInterface
$updateDate
)
:
self
{
$this
->
updateDate
=
$updateDate
;
return
$this
;
}
}
src/Entity/User.php
View file @
c219a3d5
...
...
@@ -42,7 +42,7 @@
private
$reviews
;
/**
* @ORM\
Many
ToMany(targetEntity="App\Entity\SongSpinPlay", mappedBy="user")
* @ORM\
One
ToMany(targetEntity="App\Entity\SongSpinPlay", mappedBy="user")
*/
private
$spinPlays
;
...
...
src/Repository/SongRepository.php
View file @
c219a3d5
...
...
@@ -24,12 +24,23 @@ class SongRepository extends ServiceEntityRepository
$qb
->
where
(
'e.publicationStatus = 0'
)
->
orderBy
(
'e.uploadDate'
,
'DESC'
)
->
setFirstResult
(
1
2
*
$page
)
->
setMaxResults
(
1
2
);
->
setFirstResult
(
1
0
*
$page
)
->
setMaxResults
(
1
0
);
return
$qb
->
getQuery
()
->
getResult
();
}
public
function
getHot
(
int
$page
)
{
public
function
getUpdated
(
int
$page
)
{
$qb
=
$this
->
createQueryBuilder
(
"e"
);
$qb
->
where
(
'e.publicationStatus = 0'
)
->
orderBy
(
'e.updateDate'
,
'DESC'
)
->
addOrderBy
(
'e.uploadDate'
,
'DESC'
)
->
setFirstResult
(
10
*
$page
)
->
setMaxResults
(
10
);
return
$qb
->
getQuery
()
->
getResult
();
}
public
function
getHotThisWeek
(
int
$page
)
{
$qb
=
$this
->
createQueryBuilder
(
"e"
);
$qb
->
where
(
'e.publicationStatus = 0'
)
...
...
@@ -37,13 +48,28 @@ class SongRepository extends ServiceEntityRepository
->
andWhere
(
'e.uploadDate >= :end'
)
->
orderBy
(
'e.downloads'
,
'DESC'
)
->
addOrderBy
(
'e.views'
,
'DESC'
)
->
setFirstResult
(
1
2
*
$page
)
->
setMaxResults
(
1
2
)
->
setFirstResult
(
1
0
*
$page
)
->
setMaxResults
(
1
0
)
->
setParameter
(
'begin'
,
new
\DateTime
(
'NOW'
))
->
setParameter
(
'end'
,
new
\DateTime
(
'-7 days'
));
return
$qb
->
getQuery
()
->
getResult
();
}
public
function
getHotThisMonth
(
int
$page
)
{
$qb
=
$this
->
createQueryBuilder
(
"e"
);
$qb
->
where
(
'e.publicationStatus = 0'
)
->
andWhere
(
'e.uploadDate <= :begin'
)
->
andWhere
(
'e.uploadDate >= :end'
)
->
orderBy
(
'e.downloads'
,
'DESC'
)
->
addOrderBy
(
'e.views'
,
'DESC'
)
->
setFirstResult
(
10
*
$page
)
->
setMaxResults
(
10
)
->
setParameter
(
'begin'
,
new
\DateTime
(
'NOW'
))
->
setParameter
(
'end'
,
new
\DateTime
(
'-1 month'
));
return
$qb
->
getQuery
()
->
getResult
();
}
public
function
getPopular
(
int
$page
)
{
return
[];
}
...
...
templates/apidocs/gettingstarted/introduction.html.twig
View file @
c219a3d5
...
...
@@ -26,29 +26,34 @@
<div
class=
"alert alert-primary"
role=
"alert"
>
<h4
class=
"alert-heading"
>
Last Update
</h4>
This documentation was last updated on
11/28/2020 at 15:05
CEST
This documentation was last updated on
01/22/2021 at 14:58
CEST
</div>
<div
class=
"card"
>
<h2
class=
"card-title"
>
11/28/2020
</h2>
<h2
class=
"card-title"
>
01/22/2021
</h2>
<ul>
<li>
Deprecated the Open Discovery->Popular endpoint
</li>
<li>
Added a link to the main website
</li>
<li>
Removed the Open Discovery->Popular endpoint
</li>
<li>
Added difficulty ratings to songs
</li>
<li>
Added an update hash and update time to songs
</li>
<li>
Renamed Discovery->Hot endpoint to Discovery->HotThisWeek
</li>
<li>
Added Discovery->HotThisMonth endpoint
</li>
<li>
Added Discovery->Updated endpoint
</li>
<li>
Limited the Discovery endpoint outputs to 10 per page (was 12 before)
</li>
</ul>
</div>
<div
class=
"card"
>
<h2
class=
"card-title"
>
11/
02
/2020
</h2>
<h2
class=
"card-title"
>
11/
28
/2020
</h2>
<ul>
<li>
Added preferred pronouns for user profiles
</li>
<li>
Deprecated the Open Discovery->Popular endpoint
</li>
<li>
Added a link to the main website
</li>
</ul>
</div>
<div
class=
"card"
>
<h2
class=
"card-title"
>
1
0/14
/2020
</h2>
<h2
class=
"card-title"
>
1
1/02
/2020
</h2>
<ul>
<li>
Updated Tournament output
</li>
<lI>
Added Playlists
</lI>
<li>
Added preferred pronouns for user profiles
</li>
</ul>
</div>
</div>
...
...
templates/apidocs/open/discovery.html.twig
View file @
c219a3d5
...
...
@@ -25,7 +25,14 @@
</tr>
<tr>
<th>
JSON Body
</th>
<td>
(string) searchQuery
</td>
<td>
(string) searchQuery
<br
/>
(bool) diffEasy
<br
/>
(bool) diffNormal
<br
/>
(bool) diffHard
<br
/>
(bool) diffExpert
<br
/>
(bool) diffXD
<br
/>
(int) diffRatingFrom
<br
/>
(int) diffRatingTo
</td>
</tr>
</table>
...
...
@@ -60,6 +67,12 @@
"hasHardDifficulty":false,
"hasExtremeDifficulty":false,
"hasXDDifficulty":true,
"easyDifficulty": 5,
"normalDifficulty": 10,
"hardDifficulty": 15,
"expertDifficulty": 23,
"XDDifficulty": 26,
"updateHash": "d837d2adcdc19d215130cd92f27ae927",
"cover":"https:\/\/spinsha.re\/uploads\/thumbnail\/spinshare_5f2d157f6e44a.jpg",
"zip":"https:\/\/spinsha.re\/api\/song\/1024\/download"
},
...
...
@@ -128,7 +141,7 @@
<div
class=
"card"
id=
"new"
>
<h2
class=
"card-title"
>
New Songs
</h2>
<p>
Returns the 1
2
newest songs. Use
<code
class=
"code"
>
offset
</code>
for pagination.
</p>
<p>
Returns the 1
0
newest songs. Use
<code
class=
"code"
>
offset
</code>
for pagination.
</p>
<table
class=
"table table-bordered"
>
<tr>
...
...
@@ -160,6 +173,12 @@
"hasHardDifficulty": true,
"hasExtremeDifficulty": false,
"hasXDDifficulty": false,
"easyDifficulty": 5,
"normalDifficulty": 10,
"hardDifficulty": 15,
"expertDifficulty": 23,
"XDDifficulty": 26,
"updateHash": "d837d2adcdc19d215130cd92f27ae927",
"cover":"https:\/\/spinsha.re\/uploads\/thumbnail\/spinshare_5e8df6fb90bd7.jpg",
"zip":"https:\/\/spinsha.re\/api\/song\/3\/download"
},
...
...
@@ -180,9 +199,9 @@
</pre>
</div>
<div
class=
"card"
id=
"
hot
"
>
<h2
class=
"card-title"
>
Hot
Songs
</h2>
<p>
Returns
the 12 most popular songs from the last 7 days
. Use
<code
class=
"code"
>
offset
</code>
for pagination.
</p>
<div
class=
"card"
id=
"
updated
"
>
<h2
class=
"card-title"
>
Updated
Songs
</h2>
<p>
Returns
10 songs that were last updated
. Use
<code
class=
"code"
>
offset
</code>
for pagination.
</p>
<table
class=
"table table-bordered"
>
<tr>
...
...
@@ -191,7 +210,7 @@
</tr>
<tr>
<th>
Endpoint
</th>
<td>
/songs/
hot
/
<code
class=
"code"
>
offset
</code></td>
<td>
/songs/
updated
/
<code
class=
"code"
>
offset
</code></td>
</tr>
</table>
...
...
@@ -214,6 +233,12 @@
"hasHardDifficulty": true,
"hasExtremeDifficulty": false,
"hasXDDifficulty": false,
"easyDifficulty": 5,
"normalDifficulty": 10,
"hardDifficulty": 15,
"expertDifficulty": 23,
"XDDifficulty": 26,
"updateHash": "d837d2adcdc19d215130cd92f27ae927",
"cover":"https:\/\/spinsha.re\/uploads\/thumbnail\/spinshare_5e8df6fb90bd7.jpg",
"zip":"https:\/\/spinsha.re\/api\/song\/3\/download"
},
...
...
@@ -234,9 +259,9 @@
</pre>
</div>
<div
class=
"card"
id=
"
popular
"
>
<h2
class=
"card-title"
>
Popular Songs
<span
class=
"badge badge-secondary"
>
deprecated
</span>
</h2>
<p>
Returns the 1
2 most popular songs of all time
. Use
<code
class=
"code"
>
offset
</code>
for pagination.
</p>
<div
class=
"card"
id=
"
hotThisWeek
"
>
<h2
class=
"card-title"
>
Hot This Week Songs
</h2>
<p>
Returns the 1
0 most popular songs from the last 7 days
. Use
<code
class=
"code"
>
offset
</code>
for pagination.
</p>
<table
class=
"table table-bordered"
>
<tr>
...
...
@@ -245,16 +270,115 @@
</tr>
<tr>
<th>
Endpoint
</th>
<td>
/songs/
popular
/
<code
class=
"code"
>
offset
</code></td>
<td>
/songs/
hot
/
<code
class=
"code"
>
offset
</code></td>
</tr>
</table>
<br
/><br
/>
<strong>
Output Body (Success)
</strong>
<pre
class=
"code"
>
{
"version":1,
"status":200,
"data":[
{
"id": 3,
"title": "Isabelle Singing",
"subtitle": "K.K. Bubblegum",
"artist": "K.K. Slider",
"charter": "Ellite",
"hasEasyDifficulty": false,
"hasNormalDifficulty": false,
"hasHardDifficulty": true,
"hasExtremeDifficulty": false,
"hasXDDifficulty": false,
"easyDifficulty": 5,
"normalDifficulty": 10,
"hardDifficulty": 15,
"expertDifficulty": 23,
"XDDifficulty": 26,
"updateHash": "d837d2adcdc19d215130cd92f27ae927",
"cover":"https:\/\/spinsha.re\/uploads\/thumbnail\/spinshare_5e8df6fb90bd7.jpg",
"zip":"https:\/\/spinsha.re\/api\/song\/3\/download"
},
...
]
}
</pre>
<br
/>
<div
class=
"alert alert-secondary"
role=
"alert"
>
<h4
class=
"alert-heading"
>
Deprecated
</h4>
This API-Endpoint was deprecated and should not be used anymore.
<strong>
Output Body (Not Found)
</strong>
<pre
class=
"code"
>
{
"version":1,
"status":404,
"data":[]
}
</pre>
</div>
<div
class=
"card"
id=
"hotThisMonth"
>
<h2
class=
"card-title"
>
Hot This Month Songs
</h2>
<p>
Returns the 10 most popular songs from the last month. Use
<code
class=
"code"
>
offset
</code>
for pagination.
</p>
<table
class=
"table table-bordered"
>
<tr>
<th>
Method
</th>
<td>
GET
</td>
</tr>
<tr>
<th>
Endpoint
</th>
<td>
/songs/hotThisMonth/
<code
class=
"code"
>
offset
</code></td>
</tr>
</table>
<br
/><br
/>
<strong>
Output Body (Success)
</strong>
<pre
class=
"code"
>
{
"version":1,
"status":200,
"data":[
{
"id": 3,
"title": "Isabelle Singing",
"subtitle": "K.K. Bubblegum",
"artist": "K.K. Slider",
"charter": "Ellite",
"hasEasyDifficulty": false,
"hasNormalDifficulty": false,
"hasHardDifficulty": true,
"hasExtremeDifficulty": false,
"hasXDDifficulty": false,
"easyDifficulty": 5,
"normalDifficulty": 10,
"hardDifficulty": 15,
"expertDifficulty": 23,
"XDDifficulty": 26,
"updateHash": "d837d2adcdc19d215130cd92f27ae927",
"cover":"https:\/\/spinsha.re\/uploads\/thumbnail\/spinshare_5e8df6fb90bd7.jpg",
"zip":"https:\/\/spinsha.re\/api\/song\/3\/download"
},
...
]
}
</pre>
<br
/>
<strong>
Output Body (Not Found)
</strong>
<pre
class=
"code"
>
{
"version":1,
"status":404,
"data":[]
}
</pre>
</div>
</div>
</div>
<div
class=
"col-lg-3"
>
...
...
@@ -264,8 +388,9 @@
<a
href=
"#search"
>
Search
</a>
<a
href=
"#searchAll"
>
Search All
</a>
<a
href=
"#new"
>
New Songs
</a>
<a
href=
"#hot"
>
Hot Songs
</a>
<a
href=
"#popular"
>
Popular Songs
<span
class=
"badge badge-secondary"
>
Deprecated
</span></a>
<a
href=
"#updated"
>
Updated Songs
</a>
<a
href=
"#hotThisWeek"
>
Hot This Week Songs
</a>
<a
href=
"#hotThisMonth"
>
Hot This Month Songs
</a>
</div>
</div>
</div>
...
...
templates/apidocs/open/playlists.html.twig
View file @
c219a3d5
...
...
@@ -72,11 +72,24 @@
"hasHardDifficulty": false,
"hasExtremeDifficulty": false,
"hasXDDifficulty": true,
"uploadDate": {
"date": "2020-10-14 12:50:25.000000",
"easyDifficulty": 5,
"normalDifficulty": 10,
"hardDifficulty": 15,
"expertDifficulty": 23,
"XDDifficulty": 26,
"uploadDate":
{
"date": "2021-01-15 13:27:34.000000",
"timezone_type": 3,
"timezone": "Europe/Berlin"
},
"updateDate":
{
"date": "2021-01-15 13:27:34.000000",
"timezone_type": 3,
"timezone": "Europe\
/Berlin"
"timezone": "Europe
/Berlin"
},
"updateHash": "d837d2adcdc19d215130cd92f27ae927",
"description": null
}
],
...
...
templates/apidocs/open/songs.html.twig
View file @
c219a3d5
...
...
@@ -37,46 +37,54 @@
<strong>
Output Body
</strong>
<pre
class=
"code"
>
{
"version":1,
"status":200,
"data":{
"id":3,
"title":"Isabelle Singing",
"subtitle":"K.K. Bubblegum",
"artist":"K.K. Slider",
"charter":"Ellite",
"uploader":3,
"fileReference":"spinshare_5e8df6fb90bd7",
"tags":[
"isabelle",
"singing",
"easy",
"cute",
"animal",
"crossing",
"acnh",
"new",
"horizons"
"version": 1,
"status": 200,
"data":
{
"id": 10,
"title": "Shiny Days",
"subtitle": "From \"Yuru Camp\"",
"artist": "Asaka",
"charter": "imfallin",
"uploader": 1,
"fileReference": "spinshare_60018a36ec5e9",
"tags":
[
""
],
"views":774,
"downloads":98,
"isExplicit":false,
"isTournament":false,
"hasEasyDifficulty":false,
"hasNormalDifficulty":false,
"hasHardDifficulty":true,
"hasExtremeDifficulty":false,
"hasXDDifficulty":false,
"uploadDate":{
"date":"2020-05-01 00:00:00.000000",
"timezone_type":3,
"timezone":"Europe\/Berlin"
"views": 9,
"downloads": null,
"isExplicit": false,
"publicationStatus": 0,
"hasEasyDifficulty": true,
"hasNormalDifficulty": true,
"hasHardDifficulty": true,
"hasExtremeDifficulty": true,
"hasXDDifficulty": true,
"easyDifficulty": 5,
"normalDifficulty": 10,
"hardDifficulty": 15,
"expertDifficulty": 23,
"XDDifficulty": 26,
"uploadDate":
{
"date": "2021-01-15 13:27:34.000000",
"timezone_type": 3,
"timezone": "Europe/Berlin"
},
"description":null,
"paths":{
"ogg":"https:\/\/spinsha.re\/uploads\/audio\/spinshare_5e8df6fb90bd7_0.ogg",
"cover":"https:\/\/spinsha.re\/uploads\/thumbnail\/spinshare_5e8df6fb90bd7.jpg",
"zip":"https:\/\/spinsha.re\/api\/song\/3\/download"
"updateDate":
{
"date": "2021-01-15 13:27:34.000000",
"timezone_type": 3,
"timezone": "Europe/Berlin"
},
"updateHash": "d837d2adcdc19d215130cd92f27ae927",
"description": null,
"paths":
{
"ogg": "http://localhost/www/spinshare/server/public/uploads/audio/spinshare_60018a36ec5e9_0.ogg",
"cover": "http://localhost/www/spinshare/server/public/uploads/thumbnail/spinshare_60018a36ec5e9.jpg",
"zip": "http://localhost/www/spinshare/server/public/api/song/10/download"
}
}
}
...
...
templates/base.html.twig
View file @
c219a3d5
...
...
@@ -48,6 +48,13 @@
<form
action=
"
{{
path
(
'search.index'
)
}}
"
method=
"GET"
class=
"search"
>
<input
name=
"q"
type=
"search"
placeholder=
"Search for songs, tags & profiles..."
value=
"
{{
searchQuery
|
default
(
''
)
}}
"
/>
<input
type=
"hidden"
name=
"diffEasy"
value=
"on"
/>
<input
type=
"hidden"
name=
"diffNormal"
value=
"on"
/>
<input
type=
"hidden"
name=
"diffHard"
value=
"on"
/>
<input
type=
"hidden"
name=
"diffExpert"
value=
"on"
/>
<input
type=
"hidden"
name=
"diffXD"
value=
"on"
/>
<input
type=
"hidden"
name=
"diffRatingFrom"
value=
"0"
/>
<input
type=
"hidden"
name=
"diffRatingTo"
value=
"99"
/>
</form>
<nav
class=
"items-right"
>
...
...
templates/components/song-item.html.twig
View file @
c219a3d5
...
...
@@ -8,16 +8,11 @@
<div
class=
"song-title"
>
{{
song.title
|
default
(
'Untitled'
)
}}
</div>
<div
class=
"song-artist"
>
{{
song.artist
|
default
(
'Unknown'
)
}}
</div>
<div
class=
"song-difficulties"
>
<div
class=
"difficulty
{{
song.hasEasyDifficulty
?
"active"
:
""
}}
"
><span>
E
</span></div>
<div
class=
"difficulty
{{
song.hasNormalDifficulty
?
"active"
:
""
}}
"
><span>
N
</span></div>
<div
class=
"difficulty
{{
song.hasHardDifficulty
?
"active"
:
""
}}
"
><span>
H
</span></div>
<div
class=
"difficulty
{{
song.hasExtremeDifficulty
?
"active"
:
""
}}
"
><span>
EX
</span></div>
<div
class=
"difficulty
{{
song.hasXDDifficulty
?
"active"
:
""
}}
"
><span>
XD
</span></div>
<!-- <img src="
{{
asset
(
'assets/img/difficultyEasy.svg'
)
}}
" class="
{{
song.hasEasyDifficulty
?
"active"
:
""
}}
" alt="Easy Difficulty" />
<img src="
{{
asset
(
'assets/img/difficultyNormal.svg'
)
}}
" class="
{{
song.hasNormalDifficulty
?
"active"
:
""
}}
" alt="Normal Difficulty" />
<img src="
{{
asset
(
'assets/img/difficultyHard.svg'
)
}}
" class="
{{
song.hasHardDifficulty
?
"active"
:
""
}}
" alt="Hard Difficulty" />
<img src="
{{
asset
(
'assets/img/difficultyExtreme.svg'
)
}}
" class="
{{
song.hasExtremeDifficulty
?
"active"
:
""
}}
" alt="Extreme Difficulty" />
<img src="
{{
asset
(
'assets/img/difficultyXD.svg'
)
}}
" class="
{{
song.hasXDDifficulty
?
"active"
:
""
}}
" alt="xD Difficulty" /> -->
{%
if
song.hasEasyDifficulty
%}
<div
class=
"difficulty"
><span>
E
</span>
{{
song.easyDifficulty
}}
</div>
{%
endif
%}
{%
if
song.hasNormalDifficulty
%}
<div
class=
"difficulty"
><span>
N
</span>
{{
song.normalDifficulty
}}
</div>
{%
endif
%}
{%
if
song.hasHardDifficulty
%}
<div
class=
"difficulty"
><span>
H
</span>
{{
song.hardDifficulty
}}
</div>
{%
endif
%}
{%
if
song.hasExtremeDifficulty
%}
<div
class=
"difficulty"
><span>
EX
</span>
{{
song.expertDifficulty
}}
</div>
{%
endif
%}
{%
if
song.hasXDDifficulty
%}
<div
class=
"difficulty"
><span>
XD
</span>
{{
song.XDDifficulty
}}
</div>
{%
endif
%}
</div>
</div>
</a>
\ No newline at end of file
templates/index/base.html.twig
View file @
c219a3d5
...
...
@@ -8,7 +8,9 @@
<div
class=
"tabs"
>
<a
href=
"
{{
path
(
'index.index'
)
}}
"
class=
"tab
{%
if
app.request.attributes.get
(
'_route'
)
==
'index.index'
%}
active
{%
endif
%}
"
>
Frontpage
</a>
<a
href=
"
{{
path
(
'index.new'
)
}}
"
class=
"tab
{%
if
app.request.attributes.get
(
'_route'
)
==
'index.new'
%}
active
{%
endif
%}
"
>
New
</a>
<a
href=
"
{{
path
(
'index.hot'
)
}}
"
class=
"tab
{%
if
app.request.attributes.get
(
'_route'
)
==
'index.hot'
%}
active
{%
endif
%}
"
>
Hot
</a>
<a
href=
"
{{
path
(
'index.updated'
)
}}
"
class=
"tab
{%
if
app.request.attributes.get
(
'_route'
)
==
'index.updated'
%}
active
{%
endif
%}
"
>
Updated
</a>
<a
href=
"
{{
path
(
'index.hotThisWeek'
)
}}
"
class=
"tab
{%
if
app.request.attributes.get
(
'_route'
)
==
'index.hotThisWeek'
%}
active
{%
endif
%}
"
>
Hot This Week
</a>
<a
href=
"
{{
path
(
'index.hotThisMonth'
)
}}
"
class=
"tab
{%
if
app.request.attributes.get
(
'_route'
)
==
'index.hotThisMonth'
%}
active
{%
endif
%}
"
>
Hot This Month
</a>
</div>
</header>
...
...
templates/index/hotThisMonth.html.twig
0 → 100644
View file @
c219a3d5
{%
extends
'index/base.html.twig'
%}
{%
block
indexContent
%}
<div
class=
"song-row song-row-hot"
>
<div
class=
"song-list"
>
{%
for
song
in
hotMonthSongs
%}
{{
include
(
'components/song-item.html.twig'
,
{
song
:
song
}
)
}}
{%
endfor
%}
</div>
<div
class=
"pagination"
>
{%
if
hotMonthOffset
>
0
%}
<a
href=
"
{{
path
(
'index.hot'
,
{
hotMonthOffset
:
hotMonthOffset
-
1
}
)
}}
"
class=
"button"
><i
class=
"mdi mdi-chevron-left"
></i>
PREVIOUS
</a>
{%
else
%}
<a
class=
"button button-disabled"
><i
class=
"mdi mdi-chevron-left"
></i>
PREVIOUS
</a>
{%
endif
%}
{%
if
hotMonthSongs
|
length
<
12
%}
<a
class=
"button button-disabled"
>
NEXT
<i
class=
"mdi mdi-chevron-right"
></i></a>
{%
else
%}
<a
href=
"
{{
path
(
'index.hot'
,
{
hotMonthOffset
:
hotMonthOffset
+
1
}
)
}}
"
class=
"button"
>
NEXT
<i
class=
"mdi mdi-chevron-right"
></i></a>
{%
endif
%}
</div>
</div>
{%
endblock
%}
\ No newline at end of file
templates/index/hot.html.twig
→
templates/index/hot
ThisWeek
.html.twig
View file @
c219a3d5
...
...
@@ -3,20 +3,20 @@
{%
block
indexContent
%}
<div
class=
"song-row song-row-hot"
>
<div
class=
"song-list"
>
{%
for
song
in
hotSongs
%}
{%
for
song
in
hot
Week
Songs
%}
{{
include
(
'components/song-item.html.twig'
,
{
song
:
song
}
)
}}
{%
endfor
%}
</div>
<div
class=
"pagination"
>
{%
if
hotOffset
>
0
%}
<a
href=
"
{{
path
(
'index.hot'
,
{
hot
Offset
:
hot
Offset
-
1
}
)
}}
"
class=
"button"
><i
class=
"mdi mdi-chevron-left"
></i>
PREVIOUS
</a>
{%
if
hot
Week
Offset
>
0
%}
<a
href=
"
{{
path
(
'index.hot'
,
{
hot
WeekOffset
:
hotWeek
Offset
-
1
}
)
}}
"
class=
"button"
><i
class=
"mdi mdi-chevron-left"
></i>
PREVIOUS
</a>
{%
else
%}
<a
class=
"button button-disabled"
><i
class=
"mdi mdi-chevron-left"
></i>
PREVIOUS
</a>
{%
endif
%}
{%
if
hotSongs
|
length
<
12
%}
{%
if
hot
Week
Songs
|
length
<
12
%}
<a
class=
"button button-disabled"
>
NEXT
<i
class=
"mdi mdi-chevron-right"
></i></a>
{%
else
%}
<a
href=
"
{{
path
(
'index.hot'
,
{
hot
Offset
:
hot
Offset
+
1
}
)
}}
"
class=
"button"
>
NEXT
<i
class=
"mdi mdi-chevron-right"
></i></a>
<a
href=
"
{{
path
(
'index.hot'
,
{
hot
WeekOffset
:
hotWeek
Offset
+
1
}
)
}}
"
class=
"button"
>
NEXT
<i
class=
"mdi mdi-chevron-right"
></i></a>
{%
endif
%}
</div>
</div>
...
...
templates/index/support.html.twig
View file @
c219a3d5
...
...
@@ -82,7 +82,7 @@
</div>
<div
class=
"support-subsection"
>
<h1>
Our Patr
e
ons
</h1>
<h1>
Our Patrons
</h1>
<div
class=
"user-list"
>
{%
for
user
in
patreons
%}
<a
href=
"
{{
path
(
'user.detail'
,
{
userId
:
user.id
}
)
}}
"
class=
"user-item"
>
...
...
templates/index/updated.html.twig
0 → 100644
View file @
c219a3d5
{%
extends
'index/base.html.twig'
%}
{%
block
indexContent
%}
<div
class=
"song-row song-row-new"
>
<div
class=
"song-list"
>
{%
for
song
in
updatedSongs
%}
{{
include
(
'components/song-item.html.twig'
,
{
song
:
song
}
)
}}
{%
endfor
%}
</div>
<div
class=
"pagination"
>
{%
if
updatedOffset
>
0
%}
<a
href=
"
{{
path
(
'index.new'
,
{
updatedOffset
:
updatedOffset
-
1
}
)
}}
"
class=
"button"
><i
class=
"mdi mdi-chevron-left"
></i>
PREVIOUS
</a>
{%
else
%}
<a
class=
"button button-disabled"
><i
class=
"mdi mdi-chevron-left"
></i>
PREVIOUS
</a>
{%
endif
%}
{%
if
updatedSongs
|
length
<
12
%}
<a
class=
"button button-disabled"
>
NEXT
<i
class=
"mdi mdi-chevron-right"
></i></a>
{%
else
%}
<a
href=
"
{{
path
(
'index.new'
,
{
updatedOffset
:
updatedOffset
+
1
}
)
}}
"
class=
"button"
>
NEXT
<i
class=
"mdi mdi-chevron-right"
></i></a>
{%
endif
%}
</div>
</div>
{%
endblock
%}
\ No newline at end of file
templates/moderation/migration/difficultyRating.html.twig
0 → 100644
View file @
c219a3d5
<meta
http-equiv=
"refresh"
content=
"1; URL=
{{
path
(
'moderation.system.migration.difficultyRating'
,
{
songID
:
songID
}
)
}}
"
>
Processing...
<br
/>
(Next Song ID:
{{
songID
}}
)
\ No newline at end of file
templates/moderation/migration/updateHash.html.twig
0 → 100644
View file @
c219a3d5
<meta
http-equiv=
"refresh"
content=
"1; URL=
{{
path
(
'moderation.system.migration.updateHash'
,
{
songID
:
songID
}
)
}}
"
>
Processing...
<br
/>
(Next Song ID:
{{
songID
}}
)
\ No newline at end of file
templates/moderation/system/index.html.twig
View file @
c219a3d5
...
...
@@ -17,8 +17,11 @@
<div
class=
"box"
>
Generate On All Songs Actions
<br
/><strong>
(ATTENTION: They can take a while!)
</strong><br
/><br
/>
<a
href=
"
{{
path
(
'moderation.system.generate.thumbnails'
)
}}
"
class=
"button"
>
Generate Thumbnails
</a>
<a
href=
"
{{
path
(
'moderation.system.generate.thumbnails-missing'
)
}}
"
class=
"button"
>
Generate Missing Thumbnails
</a><br
/><br
/>
<a
href=
"
{{
path
(
'moderation.system.cleanup.temp'
)
}}
"
class=
"button"
>
Cleanup Temp Folder
</a>
<a
href=
"
{{
path
(
'moderation.system.generate.thumbnails'
)
}}
"
class=
"button"
>
Generate Thumbnails
</a><br
/><br
/>
<a
href=
"
{{
path
(
'moderation.system.generate.thumbnails-missing'
)
}}
"
class=
"button"
>
Generate Missing Thumbnails
</a><br
/><br
/>
<a
href=
"
{{
path
(
'moderation.system.cleanup.temp'
)
}}
"
class=
"button"
>
Cleanup Temp Folder
</a><br
/><br
/>
<a
href=
"
{{
path
(
'moderation.system.migration.updateHash'
)
}}
"
class=
"button"
>
Regenerate Update Hashes
</a><br
/><br
/>
<a
href=
"
{{
path
(
'moderation.system.migration.difficultyRating'
)
}}
"
class=
"button"
>
Regenerate Difficulty Ratings
</a>
</div>
</section>
{%
endblock
%}
...
...
templates/search/index.html.twig
View file @
c219a3d5
...
...
@@ -4,12 +4,57 @@
{%
block
content
%}
<section
class=
"section-search"
>
<header>
<div
class=
"title"
>
Search
</div>
<div
class=
"actions"
>
<a
href=
"
{{
path
(
'search.index'
,
{
showAll
:
true
}
)
}}
"
class=
"button"
>
Show all
</a>
<div
class=
"search-filters"
>
<form
action=
""
method=
"get"
>
<input
type=
"hidden"
name=
"q"
value=
"
{{
searchQuery
}}
"
/>
<header>
Difficulties Included
</header>
<div
class=
"filter-input"
>
<label
class=
"filter-difficulty"
>
<input
type=
"checkbox"
name=
"diffEasy"
{{
filterEasy
?
'checked'
:
''
}}
/>
<span></span>
<div>
Easy
</div>
</label>
<label
class=
"filter-difficulty"
>
<input
type=
"checkbox"
name=
"diffNormal"
{{
filterNormal
?
'checked'
:
''
}}
/>
<span></span>
<div>
Normal
</div>
</label>
<label
class=
"filter-difficulty"
>
<input
type=
"checkbox"
name=
"diffHard"
{{
filterHard
?
'checked'
:
''
}}
/>
<span></span>
<div>
Hard
</div>
</label>
<label
class=
"filter-difficulty"
>
<input
type=
"checkbox"
name=
"diffExpert"
{{
filterExpert
?
'checked'
:
''
}}
/>
<span></span>
<div>
Expert
</div>
</label>
<label
class=
"filter-difficulty"
>
<input
type=
"checkbox"
name=
"diffXD"
{{
filterXD
?
'checked'
:
''
}}
/>
<span></span>
<div>
XD
</div>
</label>
</div>
<header>
Difficulty Rating
</header>
<div
class=
"filter-input"
>
<label
class=
"filter-rating"
>
<input
type=
"number"
name=
"diffRatingFrom"
value=
"
{{
diffRatingFrom
}}
"
/>
<div>
Minimal Rating
</div>
</label>
<label
class=
"filter-rating"
>
<input
type=
"number"
name=
"diffRatingTo"
value=
"
{{
diffRatingTo
}}
"
/>
<div>
Maximum Rating
</div>
</label>
</div>
</header>
<input
type=
"submit"
value=
"Filter"
/>
</form>
<!-- <a href="
{{
path
(
'search.index'
,
{
showAll
:
true
}
)
}}
" class="button">Show all</a> -->
</div>
{%
if
results
is
defined
%}
<div
class=
"search-results"
>
{%
if
results.users
|
length
==
0
and
results.songs
|
length
==
0
%}
...
...
@@ -40,7 +85,7 @@
{%
endif
%}
{%
if
results.songs
|
length
>
0
%}
<div
class=
"song-row search-results-songs"
>
<div
class=
"song-list"
>
<div
class=
"song-list
-5
"
>
{%
for
song
in
results.songs
%}
{{
include
(
'components/song-item.html.twig'
,
{
song
:
song
}
)
}}
{%
endfor
%}
...
...
templates/song/detail.html.twig
View file @
c219a3d5
...
...
@@ -84,11 +84,11 @@
<i
class=
"mdi mdi-arm-flex"
></i>
</div>
<div
class=
"difficulties"
>
<img
src=
"
{{
asset
(
'assets/img/difficultyEasy.svg'
)
}}
"
class=
"
{{
song.hasEasyDifficulty
?
"active"
:
""
}}
"
alt=
"Easy Difficulty"
/>
<img
src=
"
{{
asset
(
'assets/img/difficultyNormal.svg'
)
}}
"
class=
"
{{
song.hasNormalDifficulty
?
"active"
:
""
}}
"
alt=
"Normal Difficulty"
/>
<img
src=
"
{{
asset
(
'assets/img/difficultyHard.svg'
)
}}
"
class=
"
{{
song.hasHardDifficulty
?
"active"
:
""
}}
"
alt=
"Hard Difficulty"
/>
<img
src=
"
{{
asset
(
'assets/img/difficultyExtreme.svg'
)
}}
"
class=
"
{{
song.hasExtremeDifficulty
?
"active"
:
""
}}
"
alt=
"Extreme Difficulty"
/>
<img
src=
"
{{
asset
(
'assets/img/difficultyXD.svg'
)
}}
"
class=
"
{{
song.hasXDDifficulty
?
"active"
:
""
}}
"
alt=
"xD Difficulty"
/>
{%
if
song.hasEasyDifficulty
%}
<div
class=
"difficulty"
><span>
E
</span>
{{
song.easyDifficulty
}}
</div>
{%
endif
%}
{%
if
song.hasNormalDifficulty
%}
<div
class=
"difficulty"
><span>
N
</span>
{{
song.normalDifficulty
}}
</div>
{%
endif
%}
{%
if
song.hasHardDifficulty
%}
<div
class=
"difficulty"
><span>
H
</span>
{{
song.hardDifficulty
}}
</div>
{%
endif
%}
{%
if
song.hasExtremeDifficulty
%}
<div
class=
"difficulty"
><span>
EX
</span>
{{
song.expertDifficulty
}}
</div>
{%
endif
%}
{%
if
song.hasXDDifficulty
%}
<div
class=
"difficulty"
><span>
XD
</span>
{{
song.XDDifficulty
}}
</div>
{%
endif
%}
</div>
</div>
{%
if
song.uploadDate
|
date
(
"Y"
)
>
2000
%}
...
...
@@ -101,6 +101,16 @@
</div>
</div>
{%
endif
%}
{%
if
song.updateDate
|
date
(
"Y"
)
>
2000
%}
<div
class=
"stat"
>
<div
class=
"icon"
>
<i
class=
"mdi mdi-pencil"
></i>
</div>
<div
class=
"content"
>
{{
song.updateDate
|
date
(
"dS F Y"
)
}}
</div>
</div>
{%
endif
%}
<div
class=
"stat"
>
<div
class=
"icon"
>
<i
class=
"mdi mdi-eye"
></i>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment