diff --git a/maloja/apis/native_v1.py b/maloja/apis/native_v1.py
index 0975bf3..31e0bc9 100644
--- a/maloja/apis/native_v1.py
+++ b/maloja/apis/native_v1.py
@@ -767,6 +767,40 @@ def merge_artists(target_id,source_ids):
"status":"success"
}
+@api.post("associate_albums_to_artist")
+@authenticated_function(api=True)
+@catch_exceptions
+def associate_albums_to_artist(target_id,source_ids):
+ result = database.associate_albums_to_artist(target_id,source_ids)
+ if result:
+ return {
+ "status":"success",
+ "desc":f"{result['target']} was added as album artist of {', '.join(src['albumtitle'] for src in result['sources'])}"
+ }
+
+@api.post("associate_tracks_to_artist")
+@authenticated_function(api=True)
+@catch_exceptions
+def associate_tracks_to_artist(target_id,source_ids):
+ result = database.associate_tracks_to_artist(target_id,source_ids)
+ if result:
+ return {
+ "status":"success",
+ "desc":f"{result['target']} was added as artist for {', '.join(src['title'] for src in result['sources'])}"
+ }
+
+@api.post("associate_tracks_to_album")
+@authenticated_function(api=True)
+@catch_exceptions
+def associate_tracks_to_album(target_id,source_ids):
+ result = database.associate_tracks_to_album(target_id,source_ids)
+ if result:
+ return {
+ "status":"success",
+ "desc":f"{', '.join(src['title'] for src in result['sources'])} were added to {result['target']['albumtitle']}"
+ }
+
+
@api.post("reparse_scrobble")
@authenticated_function(api=True)
@catch_exceptions
diff --git a/maloja/database/__init__.py b/maloja/database/__init__.py
index 8969ea1..aa57c4e 100644
--- a/maloja/database/__init__.py
+++ b/maloja/database/__init__.py
@@ -265,6 +265,43 @@ def merge_albums(target_id,source_ids):
+@waitfordb
+def associate_albums_to_artist(target_id,source_ids):
+ sources = [sqldb.get_album(id) for id in source_ids]
+ target = sqldb.get_artist(target_id)
+ log(f"Adding {sources} into {target}")
+ sqldb.add_artists_to_albums(artist_ids=[target_id],album_ids=source_ids)
+ result = {'sources':sources,'target':target}
+ dbcache.invalidate_entity_cache()
+ dbcache.invalidate_caches()
+
+ return result
+
+@waitfordb
+def associate_tracks_to_artist(target_id,source_ids):
+ sources = [sqldb.get_track(id) for id in source_ids]
+ target = sqldb.get_artist(target_id)
+ log(f"Adding {sources} into {target}")
+ sqldb.add_artists_to_tracks(artist_ids=[target_id],track_ids=source_ids)
+ result = {'sources':sources,'target':target}
+ dbcache.invalidate_entity_cache()
+ dbcache.invalidate_caches()
+
+ return result
+
+@waitfordb
+def associate_tracks_to_album(target_id,source_ids):
+ sources = [sqldb.get_track(id) for id in source_ids]
+ target = sqldb.get_album(target_id)
+ log(f"Adding {sources} into {target}")
+ sqldb.add_tracks_to_albums({src:target_id for src in source_ids})
+ result = {'sources':sources,'target':target}
+ dbcache.invalidate_entity_cache()
+ dbcache.invalidate_caches()
+
+ return result
+
+
@waitfordb
def get_scrobbles(dbconn=None,**keys):
@@ -574,6 +611,18 @@ def album_info(dbconn=None,**keys):
}
+
+### TODO: FIND COOL ALGORITHM TO SELECT FEATURED STUFF
+@waitfordb
+def get_featured(dbconn=None):
+ # temporary stand-in
+ result = {
+ "artist": get_charts_artists(timerange=alltime())[0]['artist'],
+ "album": get_charts_albums(timerange=alltime())[0]['album'],
+ "track": get_charts_tracks(timerange=alltime())[0]['track']
+ }
+ return result
+
def get_predefined_rulesets(dbconn=None):
validchars = "-_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
diff --git a/maloja/database/sqldb.py b/maloja/database/sqldb.py
index acbfba9..a19954d 100644
--- a/maloja/database/sqldb.py
+++ b/maloja/database/sqldb.py
@@ -555,7 +555,7 @@ def edit_scrobble(scrobble_id,scrobbleupdatedict,dbconn=None):
dbconn.execute(op)
-
+# edit function only for primary db information (not linked fields)
@connection_provider
def edit_artist(id,artistupdatedict,dbconn=None):
@@ -578,6 +578,7 @@ def edit_artist(id,artistupdatedict,dbconn=None):
return True
+# edit function only for primary db information (not linked fields)
@connection_provider
def edit_track(id,trackupdatedict,dbconn=None):
@@ -600,6 +601,7 @@ def edit_track(id,trackupdatedict,dbconn=None):
return True
+# edit function only for primary db information (not linked fields)
@connection_provider
def edit_album(id,albumupdatedict,dbconn=None):
@@ -623,6 +625,37 @@ def edit_album(id,albumupdatedict,dbconn=None):
return True
+### Edit associations
+
+@connection_provider
+def add_artists_to_tracks(track_ids,artist_ids,dbconn=None):
+
+ op = DB['trackartists'].insert().values([
+ {'track_id':track_id,'artist_id':artist_id}
+ for track_id in track_ids for artist_id in artist_ids
+ ])
+
+ result = dbconn.execute(op)
+ clean_db(dbconn=dbconn)
+
+ return True
+
+
+@connection_provider
+def add_artists_to_albums(album_ids,artist_ids,dbconn=None):
+
+ op = DB['albumartists'].insert().values([
+ {'album_id':album_id,'artist_id':artist_id}
+ for album_id in album_ids for artist_id in artist_ids
+ ])
+
+ result = dbconn.execute(op)
+ clean_db(dbconn=dbconn)
+
+ return True
+
+
+
### Merge
@connection_provider
diff --git a/maloja/images.py b/maloja/images.py
index e576c84..ad92e68 100644
--- a/maloja/images.py
+++ b/maloja/images.py
@@ -227,6 +227,13 @@ def resolve_image(artist_id=None,track_id=None,album_id=None):
table = 'albums'
getfunc, entity_id = database.sqldb.get_album, album_id
+ if (entitytype == 'track') and malojaconfig["USE_ALBUM_ARTWORK_FOR_TRACKS"]:
+ track = database.sqldb.get_track(entity_id)
+ if track.get("album"):
+ entity_id = database.sqldb.get_album_id(track["album"])
+ entitytype = 'album'
+ getfunc = database.sqldb.get_album
+
# is another thread already working on this?
with image_resolve_controller_lock:
if entity_id in image_resolve_controller[table]:
@@ -234,6 +241,9 @@ def resolve_image(artist_id=None,track_id=None,album_id=None):
else:
image_resolve_controller[table].add(entity_id)
+
+
+
try:
entity = getfunc(entity_id)
diff --git a/maloja/pkg_global/conf.py b/maloja/pkg_global/conf.py
index e6dac70..05e8bc3 100644
--- a/maloja/pkg_global/conf.py
+++ b/maloja/pkg_global/conf.py
@@ -189,8 +189,7 @@ malojaconfig = Configuration(
"parse_remix_artists":(tp.Boolean(), "Parse Remix Artists", False)
},
"Web Interface":{
- "default_range_charts_artists":(tp.Choice({'alltime':'All Time','year':'Year','month':"Month",'week':'Week'}), "Default Range Artist Charts", "year"),
- "default_range_charts_tracks":(tp.Choice({'alltime':'All Time','year':'Year','month':"Month",'week':'Week'}), "Default Range Track Charts", "year"),
+ "default_range_startpage":(tp.Choice({'alltime':'All Time','year':'Year','month':"Month",'week':'Week'}), "Default Range for Startpage Stats", "year"),
"default_step_pulse":(tp.Choice({'year':'Year','month':"Month",'week':'Week','day':'Day'}), "Default Pulse Step", "month"),
"charts_display_tiles":(tp.Boolean(), "Display Chart Tiles", False),
"album_showcase":(tp.Boolean(), "Display Album Showcase", True, "Display a graphical album showcase for artist overview pages instead of a chart list"),
diff --git a/maloja/web/jinja/album.jinja b/maloja/web/jinja/album.jinja
index ce6ddbe..c74520d 100644
--- a/maloja/web/jinja/album.jinja
+++ b/maloja/web/jinja/album.jinja
@@ -23,6 +23,9 @@
{% include 'icons/merge.jinja' %}
{% include 'icons/merge_mark.jinja' %}
{% include 'icons/merge_cancel.jinja' %}
+ {% include 'icons/add_album.jinja' %}
+ {% include 'icons/association_mark.jinja' %}
+ {% include 'icons/association_cancel.jinja' %}
{% endif %}
{% endblock %}
diff --git a/maloja/web/jinja/artist.jinja b/maloja/web/jinja/artist.jinja
index f6b22d6..87597de 100644
--- a/maloja/web/jinja/artist.jinja
+++ b/maloja/web/jinja/artist.jinja
@@ -33,6 +33,7 @@
{% include 'icons/merge.jinja' %}
{% include 'icons/merge_mark.jinja' %}
{% include 'icons/merge_cancel.jinja' %}
+ {% include 'icons/add_artist.jinja' %}
{% endif %}
{% endblock %}
diff --git a/maloja/web/jinja/icons/add_album.jinja b/maloja/web/jinja/icons/add_album.jinja
new file mode 100644
index 0000000..11d2d67
--- /dev/null
+++ b/maloja/web/jinja/icons/add_album.jinja
@@ -0,0 +1,6 @@
+
diff --git a/maloja/web/jinja/icons/add_album_confirm.jinja b/maloja/web/jinja/icons/add_album_confirm.jinja
new file mode 100644
index 0000000..2d50fb2
--- /dev/null
+++ b/maloja/web/jinja/icons/add_album_confirm.jinja
@@ -0,0 +1,5 @@
+
diff --git a/maloja/web/jinja/icons/add_artist.jinja b/maloja/web/jinja/icons/add_artist.jinja
new file mode 100644
index 0000000..07a958f
--- /dev/null
+++ b/maloja/web/jinja/icons/add_artist.jinja
@@ -0,0 +1,5 @@
+
diff --git a/maloja/web/jinja/icons/add_artist_confirm.jinja b/maloja/web/jinja/icons/add_artist_confirm.jinja
new file mode 100644
index 0000000..44bad86
--- /dev/null
+++ b/maloja/web/jinja/icons/add_artist_confirm.jinja
@@ -0,0 +1,5 @@
+
diff --git a/maloja/web/jinja/icons/association_cancel.jinja b/maloja/web/jinja/icons/association_cancel.jinja
new file mode 100644
index 0000000..a8d9615
--- /dev/null
+++ b/maloja/web/jinja/icons/association_cancel.jinja
@@ -0,0 +1,7 @@
+
diff --git a/maloja/web/jinja/icons/association_mark.jinja b/maloja/web/jinja/icons/association_mark.jinja
new file mode 100644
index 0000000..6b68dae
--- /dev/null
+++ b/maloja/web/jinja/icons/association_mark.jinja
@@ -0,0 +1,5 @@
+
diff --git a/maloja/web/jinja/icons/nodata.jinja b/maloja/web/jinja/icons/nodata.jinja
index dde0cbb..8ddb3d1 100644
--- a/maloja/web/jinja/icons/nodata.jinja
+++ b/maloja/web/jinja/icons/nodata.jinja
@@ -1,7 +1,7 @@
-
+
+
diff --git a/maloja/web/jinja/partials/charts_albums_tiles.jinja b/maloja/web/jinja/partials/charts_albums_tiles.jinja
index 400d68b..86f7ba2 100644
--- a/maloja/web/jinja/partials/charts_albums_tiles.jinja
+++ b/maloja/web/jinja/partials/charts_albums_tiles.jinja
@@ -6,40 +6,26 @@
{% endif %}
{% set charts_14 = charts | fixlength(14) %}
-{% set charts_cycler = cycler(*charts_14) %}
-
-
-{% for segment in range(3) %}
- {% if charts_14[0] is none and loop.first %}
- {% include 'icons/nodata.jinja' %}
- {% else %}
-
- {% set segmentsize = segment+1 %}
-
- {% for row in range(segmentsize) -%}
-
- {% for col in range(segmentsize) %}
- {% set entry = charts_cycler.next() %}
- {% if entry is not none %}
- {% set album = entry.album %}
- {% set rank = entry.rank %}
-
-
-
- #{{ rank }} {{ album.albumtitle }}
-
-
-
- {% else -%}
-
- {%- endif -%}
- {%- endfor -%}
-
- {%- endfor -%}
-
-
+
+ {% if charts_14[0] is none %}
+ {% include 'icons/nodata.jinja' %}
{% endif %}
-{% endfor %}
-
+ {% for entry in charts_14 %}
+ {% if entry is not none %}
+ {% set album = entry.album %}
+ {% set rank = entry.rank %}
+
+ {% else %}
+
+ {% endif %}
+ {% endfor %}
+
+
diff --git a/maloja/web/jinja/partials/charts_artists_tiles.jinja b/maloja/web/jinja/partials/charts_artists_tiles.jinja
index 23ac18e..ef97ede 100644
--- a/maloja/web/jinja/partials/charts_artists_tiles.jinja
+++ b/maloja/web/jinja/partials/charts_artists_tiles.jinja
@@ -6,40 +6,26 @@
{% endif %}
{% set charts_14 = charts | fixlength(14) %}
-{% set charts_cycler = cycler(*charts_14) %}
-
-
-{% for segment in range(3) %}
- {% if charts_14[0] is none and loop.first %}
- {% include 'icons/nodata.jinja' %}
- {% else %}
-
- {% set segmentsize = segment+1 %}
-
- {% for row in range(segmentsize) -%}
-
- {% for col in range(segmentsize) %}
- {% set entry = charts_cycler.next() %}
- {% if entry is not none %}
- {% set artist = entry.artist %}
- {% set rank = entry.rank %}
-
-
-
- #{{ rank }} {{ artist }}
-
-
-
- {% else -%}
-
- {%- endif -%}
- {%- endfor -%}
-
- {%- endfor -%}
-
-
+
+ {% if charts_14[0] is none %}
+ {% include 'icons/nodata.jinja' %}
{% endif %}
-{% endfor %}
-
+ {% for entry in charts_14 %}
+ {% if entry is not none %}
+ {% set artist = entry.artist %}
+ {% set rank = entry.rank %}
+
+ {% else %}
+
+ {% endif %}
+ {% endfor %}
+
+
diff --git a/maloja/web/jinja/partials/charts_tracks_tiles.jinja b/maloja/web/jinja/partials/charts_tracks_tiles.jinja
index ba66a10..6b238a6 100644
--- a/maloja/web/jinja/partials/charts_tracks_tiles.jinja
+++ b/maloja/web/jinja/partials/charts_tracks_tiles.jinja
@@ -6,39 +6,26 @@
{% endif %}
{% set charts_14 = charts | fixlength(14) %}
-{% set charts_cycler = cycler(*charts_14) %}
-
-{% for segment in range(3) %}
- {% if charts_14[0] is none and loop.first %}
- {% include 'icons/nodata.jinja' %}
- {% else %}
-
- {% set segmentsize = segment+1 %}
-
- {% for row in range(segmentsize) -%}
-
- {% for col in range(segmentsize) %}
- {% set entry = charts_cycler.next() %}
- {% if entry is not none %}
- {% set track = entry.track %}
- {% set rank = entry.rank %}
-
-
-
- #{{ rank }} {{ track.title }}
-
-
-
- {% else -%}
-
- {%- endif %}
- {%- endfor -%}
-
- {%- endfor %}
-
-
+
+ {% if charts_14[0] is none %}
+ {% include 'icons/nodata.jinja' %}
{% endif %}
-{% endfor %}
-
+ {% for entry in charts_14 %}
+ {% if entry is not none %}
+ {% set track = entry.track %}
+ {% set rank = entry.rank %}
+
+ {% else %}
+
+ {% endif %}
+ {% endfor %}
+
+
diff --git a/maloja/web/jinja/partials/info_album.jinja b/maloja/web/jinja/partials/info_album.jinja
new file mode 100644
index 0000000..95a9ca9
--- /dev/null
+++ b/maloja/web/jinja/partials/info_album.jinja
@@ -0,0 +1,45 @@
+{% import 'snippets/links.jinja' as links %}
+{% import 'partials/awards_album.jinja' as awards %}
+
+{% set album = filterkeys.album %}
+{% set info = dbc.album_info({'album':album}) %}
+
+{% set encodedalbum = mlj_uri.uriencode({'album':album}) %}
+
+
+
+
+
+ {% if adminmode %}
+
+ {% else %}
+
+
+ {% endif %}
+
+
+ {{ links.links(album.artists) }}
+ {{ info.album.albumtitle | e }}
+ {# awards.certs(album) #}
+ #{{ info.position }}
+
+
+
+ {{ info['scrobbles'] }} Scrobbles
+
+
+
+
+
+
+ {{ awards.medals(info) }}
+ {{ awards.topweeks(info) }}
+
+
+
+
+
diff --git a/maloja/web/jinja/partials/info_artist.jinja b/maloja/web/jinja/partials/info_artist.jinja
new file mode 100644
index 0000000..f3308a9
--- /dev/null
+++ b/maloja/web/jinja/partials/info_artist.jinja
@@ -0,0 +1,59 @@
+{% import 'snippets/links.jinja' as links %}
+{% import 'partials/awards_artist.jinja' as awards %}
+
+
+{% set artist = filterkeys.artist %}
+{% set info = db.artist_info(artist=artist) %}
+
+{% set credited = info.get('replace') %}
+{% set included = info.get('associated') %}
+
+{% if credited is not none %}
+ {% set competes = false %}
+{% else %}
+ {% set credited = artist %}
+ {% set competes = true %}
+{% endif %}
+
+
+
+
+
+ {% if adminmode %}
+
+ {% else %}
+
+
+ {% endif %}
+
+
+ {{ info.artist | e }}
+ {% if competes and info['scrobbles']>0 %}#{{ info.position }} {% endif %}
+
+ {% if competes and included %}
+ associated: {{ links.links(included) }}
+ {% elif not competes %}
+ Competing under {{ links.link(credited) }} (#{{ info.position }})
+ {% endif %}
+
+
+ {{ info['scrobbles'] }} Scrobbles
+
+
+
+
+
+ {% if competes %}
+ {{ awards.medals(info) }}
+ {{ awards.topweeks(info) }}
+ {% endif %}
+ {{ awards.certs(artist) }}
+
+
+
+
+
diff --git a/maloja/web/jinja/partials/info_track.jinja b/maloja/web/jinja/partials/info_track.jinja
new file mode 100644
index 0000000..f46e342
--- /dev/null
+++ b/maloja/web/jinja/partials/info_track.jinja
@@ -0,0 +1,48 @@
+{% import 'snippets/links.jinja' as links %}
+
+{% set track = filterkeys.track %}
+{% set info = dbc.track_info({'track':track}) %}
+
+{% import 'partials/awards_track.jinja' as awards %}
+
+
+
+
+
+ {% if adminmode %}
+
+ {% else %}
+
+
+ {% endif %}
+
+
+ {{ links.links(track.artists) }}
+ {{ info.track.title | e }}
+ {{ awards.certs(track) }}
+ #{{ info.position }}
+
+ {% if info.track.album %}
+ from {{ links.link(info.track.album) }}
+ {% endif %}
+
+
+ {% if adminmode %}Scrobble now {% endif %}
+ {{ info['scrobbles'] }} Scrobbles
+
+
+
+
+
+
+ {{ awards.medals(info) }}
+ {{ awards.topweeks(info) }}
+
+
+
+
+
diff --git a/maloja/web/jinja/snippets/links.jinja b/maloja/web/jinja/snippets/links.jinja
index 3b5c29d..f90a535 100644
--- a/maloja/web/jinja/snippets/links.jinja
+++ b/maloja/web/jinja/snippets/links.jinja
@@ -1,5 +1,5 @@
{% macro link(entity) -%}
- {% if entity is mapping and 'title' in entity or 'albumtitle' in entity %}
+ {% if entity is mapping and ('title' in entity or 'albumtitle' in entity) %}
{% set name = entity.title or entity.albumtitle %}
{% else %}
{% set name = entity %}
diff --git a/maloja/web/jinja/start.jinja b/maloja/web/jinja/start.jinja
index f8240da..eb22b37 100644
--- a/maloja/web/jinja/start.jinja
+++ b/maloja/web/jinja/start.jinja
@@ -3,109 +3,44 @@
{% block scripts %}
-
-
+
+
+
+
{% endblock %}
{% block content -%}
-
-
-
-
- {% for r in xcurrent -%}
-
- {{ r.localisation }}
-
- {{ "|" if not loop.last }}
- {%- endfor %}
-
-
+
- {% for r in xcurrent -%}
-
- {%- with limitkeys = {"timerange":r.range} -%}
- {% include 'partials/charts_artists_tiles.jinja' %}
- {%- endwith -%}
-
- {%- endfor %}
+ {% for module in ['charts_artists','charts_tracks','charts_albums','pulse','lastscrobbles','featured'] %}
+
+
+
+ {% include 'startpage_modules/' + module + '.jinja' %}
+
+
+ {% endfor %}
+
-
-
-
- {% for r in xcurrent -%}
-
- {{ r.localisation }}
-
- {{ "|" if not loop.last }}
- {%- endfor %}
-
-
-
-
- {% for r in xcurrent -%}
-
- {%- with limitkeys = {"timerange":r.range} -%}
- {% include 'partials/charts_tracks_tiles.jinja' %}
- {%- endwith -%}
-
- {%- endfor %}
-
-
-
-
-
-
-
- {% for range in xcurrent %}
-
{{ range.localisation }}
-
{{ dbc.get_scrobbles_num({'timerange':range.range}) }}
- {% endfor %}
-
-
-
-
-
- {%- with amountkeys = {"perpage":12,"page":0}, shortTimeDesc=True -%}
- {% include 'partials/scrobbles.jinja' %}
- {%- endwith -%}
-
-
-
-
-
-
-
-
-
-
-
- {% for range in xranges -%}
-
- {{ range.localisation }}
-
- {{ "|" if not loop.last }}
- {%- endfor %}
-
-
- {% for range in xranges -%}
-
- {%- with limitkeys={"since":range.firstrange},delimitkeys={"step":range.identifier} -%}
- {% include 'partials/pulse.jinja' %}
- {%- endwith -%}
-
- {%- endfor %}
-
-
-
{%- endblock %}
diff --git a/maloja/web/jinja/startpage_modules/charts_albums.jinja b/maloja/web/jinja/startpage_modules/charts_albums.jinja
new file mode 100644
index 0000000..671fe10
--- /dev/null
+++ b/maloja/web/jinja/startpage_modules/charts_albums.jinja
@@ -0,0 +1,19 @@
+
+
+{% for r in xcurrent -%}
+
+ {{ r.localisation }}
+
+ {{ "|" if not loop.last }}
+{%- endfor %}
+
+
+
+
+{% for r in xcurrent -%}
+
+ {%- with limitkeys = {"timerange":r.range} -%}
+ {% include 'partials/charts_albums_tiles.jinja' %}
+ {%- endwith -%}
+
+{%- endfor %}
diff --git a/maloja/web/jinja/startpage_modules/charts_artists.jinja b/maloja/web/jinja/startpage_modules/charts_artists.jinja
new file mode 100644
index 0000000..0c78c89
--- /dev/null
+++ b/maloja/web/jinja/startpage_modules/charts_artists.jinja
@@ -0,0 +1,19 @@
+
+
+{% for r in xcurrent -%}
+
+ {{ r.localisation }}
+
+ {{ "|" if not loop.last }}
+{%- endfor %}
+
+
+
+
+{% for r in xcurrent -%}
+
+ {%- with limitkeys = {"timerange":r.range} -%}
+ {% include 'partials/charts_artists_tiles.jinja' %}
+ {%- endwith -%}
+
+{%- endfor %}
diff --git a/maloja/web/jinja/startpage_modules/charts_tracks.jinja b/maloja/web/jinja/startpage_modules/charts_tracks.jinja
new file mode 100644
index 0000000..4ae6e4c
--- /dev/null
+++ b/maloja/web/jinja/startpage_modules/charts_tracks.jinja
@@ -0,0 +1,19 @@
+
+
+{% for r in xcurrent -%}
+
+ {{ r.localisation }}
+
+ {{ "|" if not loop.last }}
+{%- endfor %}
+
+
+
+
+{% for r in xcurrent -%}
+
+ {%- with limitkeys = {"timerange":r.range} -%}
+ {% include 'partials/charts_tracks_tiles.jinja' %}
+ {%- endwith -%}
+
+{%- endfor %}
diff --git a/maloja/web/jinja/startpage_modules/featured.jinja b/maloja/web/jinja/startpage_modules/featured.jinja
new file mode 100644
index 0000000..bfc2bcb
--- /dev/null
+++ b/maloja/web/jinja/startpage_modules/featured.jinja
@@ -0,0 +1,62 @@
+
+
+Featured
+
+{% set featured = dbc.get_featured() %}
+
+{% set entitytypes = [
+ {'identifier':'artist','localisation':"Artist", 'template':"info_artist.jinja", 'filterkeys':{"artist": featured.artist } },
+ {'identifier':'track','localisation':"Track", 'template':"info_track.jinja", 'filterkeys':{"track": featured.track } },
+ {'identifier':'album','localisation':"Album", 'template':"info_album.jinja", 'filterkeys':{"album": featured.album } }
+] %}
+
+
+{% for t in entitytypes -%}
+
+ {{ t.localisation }}
+
+ {{ "|" if not loop.last }}
+{%- endfor %}
+
+
+
+
+{% for t in entitytypes -%}
+
+ {%- with filterkeys = t.filterkeys -%}
+
+ {% include 'partials/' + t.template %}
+ {%- endwith -%}
+
+{%- endfor %}
+
+
+
+
+
diff --git a/maloja/web/jinja/startpage_modules/lastscrobbles.jinja b/maloja/web/jinja/startpage_modules/lastscrobbles.jinja
new file mode 100644
index 0000000..ae0da70
--- /dev/null
+++ b/maloja/web/jinja/startpage_modules/lastscrobbles.jinja
@@ -0,0 +1,15 @@
+
+
+{% for range in xcurrent %}
+ {{ range.localisation }}
+ {{ dbc.get_scrobbles_num({'timerange':range.range}) }}
+{% endfor %}
+
+
+
+
+
+ {%- with amountkeys = {"perpage":12,"page":0}, shortTimeDesc=True -%}
+ {% include 'partials/scrobbles.jinja' %}
+ {%- endwith -%}
+
diff --git a/maloja/web/jinja/startpage_modules/pulse.jinja b/maloja/web/jinja/startpage_modules/pulse.jinja
new file mode 100644
index 0000000..5efdc00
--- /dev/null
+++ b/maloja/web/jinja/startpage_modules/pulse.jinja
@@ -0,0 +1,17 @@
+
+
+{% for range in xranges -%}
+
+ {{ range.localisation }}
+
+ {{ "|" if not loop.last }}
+{%- endfor %}
+
+
+{% for range in xranges -%}
+
+ {%- with limitkeys={"since":range.firstrange},delimitkeys={"step":range.identifier} -%}
+ {% include 'partials/pulse.jinja' %}
+ {%- endwith -%}
+
+{%- endfor %}
diff --git a/maloja/web/jinja/track.jinja b/maloja/web/jinja/track.jinja
index 85f0c10..f426fd3 100644
--- a/maloja/web/jinja/track.jinja
+++ b/maloja/web/jinja/track.jinja
@@ -28,6 +28,8 @@
{% include 'icons/merge.jinja' %}
{% include 'icons/merge_mark.jinja' %}
{% include 'icons/merge_cancel.jinja' %}
+ {% include 'icons/association_mark.jinja' %}
+ {% include 'icons/association_cancel.jinja' %}
{% endif %}
{% endblock %}
diff --git a/maloja/web/static/css/maloja.css b/maloja/web/static/css/maloja.css
index 239b44a..f46bc1e 100644
--- a/maloja/web/static/css/maloja.css
+++ b/maloja/web/static/css/maloja.css
@@ -209,7 +209,7 @@ div#notification_area {
div#notification_area div.notification {
background-color:white;
width:400px;
- height:50px;
+ min-height:50px;
margin-bottom:7px;
padding:9px;
opacity:0.4;
@@ -800,53 +800,78 @@ table.misc td {
+div.tiles {
+ display: grid;
+ grid-template-columns: repeat(18, calc(100% / 18));
+ grid-template-rows: repeat(6, calc(100% / 6));
+ grid-auto-flow: row dense;
+ aspect-ratio: 3 / 1;
-
-
-table.tiles_top td {
- padding:0px;
- border:0px;
}
-table.tiles_top:hover td td {
+div.tiles div {
+ height: 100%;
+ width: 100%;
+ background-size: cover;
+ background-position:top center;
+ overflow: hidden;
+}
+
+div.tiles:hover div {
opacity:0.5;
filter: grayscale(80%);
}
-table.tiles_top:hover td td:hover {
+div.tiles:hover div:hover {
opacity:1;
filter: grayscale(0%);
}
-table.tiles_top, table.tiles_sub {
- border-collapse: collapse;
+div.tiles div.tile:nth-child(1) {
+ grid-column: span 6;
+ grid-row: span 6;
+
+ font-size:100%
+}
+
+div.tiles div.tile:nth-child(2),
+div.tiles div.tile:nth-child(3),
+div.tiles div.tile:nth-child(4),
+div.tiles div.tile:nth-child(5) {
+ grid-column: span 3;
+ grid-row: span 3;
+
+ font-size:80%
+}
+
+div.tiles div.tile:nth-child(2),
+div.tiles div.tile:nth-child(4) {
+ grid-column-start: 7;
+ grid-column-end: span 3;
+}
+div.tiles div.tile:nth-child(3),
+div.tiles div.tile:nth-child(5) {
+ grid-column-start: 10;
+ grid-column-end: span 3;
+}
+
+div.tiles div.tile:nth-child(6),
+div.tiles div.tile:nth-child(7),
+div.tiles div.tile:nth-child(8),
+div.tiles div.tile:nth-child(9),
+div.tiles div.tile:nth-child(10),
+div.tiles div.tile:nth-child(11),
+div.tiles div.tile:nth-child(12),
+div.tiles div.tile:nth-child(13),
+div.tiles div.tile:nth-child(14) {
+ grid-column: span 2;
+ grid-row: span 2;
+
+ font-size:60%
}
-
-table.tiles_top>tbody>tr>td {
- height:300px;
- width:300px;
-}
-
-
-table.tiles_sub {
- height:100%;
- width:100%;
-}
-
-table.tiles_sub div {
- height:100%;
- width:100%;
-}
-
-table.tiles_top td div {
- background-size:cover;
- background-position:top center;
- vertical-align:bottom;
-}
-
-table.tiles_top td span {
+div.tiles span {
background-color:rgba(0,0,0,0.7);
display: inline-block;
margin-top:2%;
@@ -854,48 +879,21 @@ table.tiles_top td span {
max-width: 67%;
vertical-align: text-top;
}
-table.tiles_top td a:hover {
+div.tiles span {
+ overflow-wrap: anywhere;
+}
+
+div.tiles a:hover {
text-decoration: none;
}
-table.tiles_1x1 td {
- height:100%;
- width:100%;
- font-size:100%
-}
-table.tiles_2x2 td {
- height:50%;
- width:50%;
- font-size:80%
-}
-table.tiles_3x3 td {
- height:33.333%;
- width:33.333%;
- font-size:60%
-}
-table.tiles_4x4 td {
- font-size:50%
-}
-table.tiles_5x5 td {
- font-size:40%
-}
-/* Safari fix */
-table.tiles_sub.tiles_3x3 td div {
- min-height: 100px;
- min-width: 100px;
-}
-table.tiles_sub.tiles_2x2 td div {
- min-height: 150px;
- min-width: 150px;
-}
-table.tiles_sub.tiles_1x1 td div {
- min-height: 300px;
- min-width: 300px;
-}
-table.tiles_sub a span {
- overflow-wrap: anywhere;
-}
+
+
+
+
+
+
div#showcase_container {
@@ -986,35 +984,3 @@ span.stat_module_pulse, span.stat_module_topartists, span.stat_module_toptracks
display: inline-block;
vertical-align: top;
}
-
-/*
-**
-**
-** SIDE LIST ON START PAGE
-**
-**
-*/
-
-
-@media (min-width: 1600px) {
-div.sidelist {
- position:absolute;
- right:0px;
- top:0px;
- width:40%;
- height:100%;
- --current-bg-color: var(--base-color-light); /* drag this information through inheritances */
- background-color: var(--current-bg-color);
- padding-left:30px;
- padding-right:30px;
-}
-}
-
-div.sidelist table {
- width:100%;
- table-layout:fixed;
-}
-
-div.sidelist table.list td.time {
- width:17%;
-}
diff --git a/maloja/web/static/css/startpage.css b/maloja/web/static/css/startpage.css
new file mode 100644
index 0000000..2d3fe9b
--- /dev/null
+++ b/maloja/web/static/css/startpage.css
@@ -0,0 +1,49 @@
+div#startpage {
+ display: grid;
+ justify-content: center;
+
+ display: fixed;
+ grid-column-gap: 25px;
+ grid-row-gap: 25px;
+}
+
+
+@media (min-width: 2201px) {
+ div#startpage {
+ grid-template-columns: 30vw 30vw 30vw;
+ grid-template-rows: 45vh 45vh;
+
+ grid-template-areas:
+ "charts_artists charts_tracks charts_albums"
+ "lastscrobbles featured pulse";
+ }
+}
+
+@media (min-width: 1401px) and (max-width: 2200px) {
+ div#startpage {
+ grid-template-columns: 45vw 45vw;
+ grid-template-rows: 45vh 45vh 45vh;
+
+ grid-template-areas:
+ "charts_artists lastscrobbles"
+ "charts_tracks pulse"
+ "charts_albums featured";
+ }
+}
+
+@media (max-width: 1400px) {
+ div#startpage {
+ grid-template-columns: 90vw;
+
+ grid-template-areas:
+ "charts_artists"
+ "charts_tracks"
+ "charts_albums"
+ "lastscrobbles"
+ "pulse";
+ }
+
+ #start_page_module_featured {
+ display: none;
+ }
+}
diff --git a/maloja/web/static/js/edit.js b/maloja/web/static/js/edit.js
index e25f9c9..501ae7b 100644
--- a/maloja/web/static/js/edit.js
+++ b/maloja/web/static/js/edit.js
@@ -190,6 +190,8 @@ function doneEditing() {
// MERGING
function showValidMergeIcons() {
+
+ // merge
const lcst = window.sessionStorage;
var key = "marked_for_merge_" + entity_type;
var current_stored = (lcst.getItem(key) || '').split(",");
@@ -218,6 +220,81 @@ function showValidMergeIcons() {
}
}
+ // mark for association
+ if ((entity_type == 'track') || (entity_type == 'album')) {
+ const lcst = window.sessionStorage;
+ var key = "marked_for_associate_" + entity_type;
+ var current_stored = (lcst.getItem(key) || '').split(",");
+ current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x));
+
+ var associationmarkicon = document.getElementById('associationmarkicon');
+ var associationcancelicon = document.getElementById('associationcancelicon');
+
+ associationmarkicon.classList.add('hide');
+ associationcancelicon.classList.add('hide');
+
+
+ if (current_stored.length == 0) {
+ associationmarkicon.classList.remove('hide');
+ }
+ else {
+ associationcancelicon.classList.remove('hide');
+
+ if (current_stored.includes(entity_id)) {
+
+ }
+ else {
+ associationmarkicon.classList.remove('hide');
+ }
+ }
+
+ if (entity_type == 'track') {
+ associationmarkicon.title = "Mark this track to add to album or add artist";
+ }
+ else {
+ associationmarkicon.title = "Mark this album to add artist";
+ }
+ }
+
+
+
+ // association confirm
+ if ((entity_type == 'artist') || (entity_type == 'album')) {
+ var target_entity_types = {artist:['album','track'], album:['track']};
+ var to_associate = {};
+ var to_associate_all = [];
+ for (var target_entity_type of target_entity_types[entity_type]) {
+ const lcst = window.sessionStorage;
+ var key = "marked_for_associate_" + target_entity_type;
+ var current_stored = (lcst.getItem(key) || '').split(",");
+ to_associate[target_entity_type] = current_stored.filter((x)=>x).map((x)=>parseInt(x));
+ to_associate_all = to_associate_all.concat(to_associate[target_entity_type]);
+ }
+
+ var associateicon = document.getElementById('associate' + entity_type + 'icon');
+
+ associateicon.classList.add('hide');
+
+
+ if (to_associate_all.length == 0) {
+
+ }
+ else {
+ associateicon.classList.remove('hide');
+ if (entity_type == 'artist') {
+ associateicon.title = "Add this artist to " + to_associate['album'].length + " albums and " + to_associate['track'].length + " tracks";
+ }
+ else {
+ associateicon.title = "Add " + to_associate['track'].length + " tracks to this album";
+ }
+
+ }
+ }
+
+
+
+
+
}
@@ -233,6 +310,19 @@ function markForMerge() {
showValidMergeIcons();
}
+function markForAssociate() {
+ const lcst = window.sessionStorage;
+ var key = "marked_for_associate_" + entity_type;
+ var current_stored = (lcst.getItem(key) || '').split(",");
+ current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x));
+ current_stored.push(entity_id);
+ current_stored = [...new Set(current_stored)];
+ lcst.setItem(key,current_stored); //this already formats it correctly
+ var whattoadd = ((entity_type == 'track') ? "Artists or Album" : "Artists")
+ notify("Marked " + entity_name + " to add " + whattoadd,"Currently " + current_stored.length + " marked!")
+ showValidMergeIcons();
+}
+
function merge() {
const lcst = window.sessionStorage;
var key = "marked_for_merge_" + entity_type;
@@ -262,6 +352,47 @@ function merge() {
lcst.removeItem(key);
}
+
+
+function associate() {
+ const lcst = window.sessionStorage;
+ var target_entity_types = {artist:['album','track'], album:['track']};
+ for (var target_entity_type of target_entity_types[entity_type]) {
+ var key = "marked_for_associate_" + target_entity_type;
+ console.log('get',key);
+ var current_stored = (lcst.getItem(key) || '').split(",");
+ current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x));
+
+ if (current_stored.length != 0) {
+ callback_func = function(req){
+ if (req.status == 200) {
+ //window.location.reload();
+ showValidMergeIcons();
+ notifyCallback(req);
+ }
+ else {
+ notifyCallback(req);
+ }
+ };
+
+ neo.xhttpreq(
+ "/apis/mlj_1/associate_" + target_entity_type + "s_to_" + entity_type,
+ data={
+ 'source_ids':current_stored,
+ 'target_id':entity_id
+ },
+ method="POST",
+ callback=callback_func,
+ json=true
+ );
+
+ lcst.removeItem(key);
+ }
+
+ }
+
+}
+
function cancelMerge() {
const lcst = window.sessionStorage;
var key = "marked_for_merge_" + entity_type;
@@ -269,3 +400,10 @@ function cancelMerge() {
showValidMergeIcons();
notify("Cancelled merge!","")
}
+function cancelAssociate() {
+ const lcst = window.sessionStorage;
+ var key = "marked_for_associate_" + entity_type;
+ lcst.setItem(key,[]);
+ showValidMergeIcons();
+ notify("Cancelled association!","")
+}
diff --git a/maloja/web/static/js/rangeselect.js b/maloja/web/static/js/rangeselect.js
index f42d789..2561a69 100644
--- a/maloja/web/static/js/rangeselect.js
+++ b/maloja/web/static/js/rangeselect.js
@@ -2,7 +2,7 @@ localStorage = window.localStorage;
function showRange(identifier,unit) {
// Make all modules disappear
- modules = document.getElementsByClassName("stat_module_" + identifier);
+ var modules = document.getElementsByClassName("stat_module_" + identifier);
for (var i=0;i