mirror of
https://github.com/krateng/maloja.git
synced 2025-04-15 16:30:32 +03:00
Added disassociation functionality
This commit is contained in:
parent
ad824626c3
commit
a3831f9b7c
@ -774,23 +774,25 @@ def merge_artists(target_id,source_ids):
|
||||
@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)
|
||||
def associate_albums_to_artist(target_id,source_ids,remove=False):
|
||||
result = database.associate_albums_to_artist(target_id,source_ids,remove=remove)
|
||||
descword = "removed" if remove else "added"
|
||||
if result:
|
||||
return {
|
||||
"status":"success",
|
||||
"desc":f"{result['target']} was added as album artist of {', '.join(src['albumtitle'] for src in result['sources'])}"
|
||||
"desc":f"{result['target']} was {descword} 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)
|
||||
def associate_tracks_to_artist(target_id,source_ids,remove=False):
|
||||
result = database.associate_tracks_to_artist(target_id,source_ids,remove=remove)
|
||||
descword = "removed" if remove else "added"
|
||||
if result:
|
||||
return {
|
||||
"status":"success",
|
||||
"desc":f"{result['target']} was added as artist for {', '.join(src['title'] for src in result['sources'])}"
|
||||
"desc":f"{result['target']} was {descword} as artist for {', '.join(src['title'] for src in result['sources'])}"
|
||||
}
|
||||
|
||||
@api.post("associate_tracks_to_album")
|
||||
@ -801,7 +803,7 @@ def 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']}"
|
||||
"desc":f"{', '.join(src['title'] for src in result['sources'])} were " + f"added to {result['target']['albumtitle']}" if target_id else "removed from their album"
|
||||
}
|
||||
|
||||
|
||||
|
@ -269,11 +269,15 @@ def merge_albums(target_id,source_ids):
|
||||
|
||||
|
||||
@waitfordb
|
||||
def associate_albums_to_artist(target_id,source_ids):
|
||||
def associate_albums_to_artist(target_id,source_ids,remove=False):
|
||||
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)
|
||||
if remove:
|
||||
log(f"Removing {sources} from {target}")
|
||||
sqldb.remove_artists_from_albums(artist_ids=[target_id],album_ids=source_ids)
|
||||
else:
|
||||
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()
|
||||
@ -281,11 +285,15 @@ def associate_albums_to_artist(target_id,source_ids):
|
||||
return result
|
||||
|
||||
@waitfordb
|
||||
def associate_tracks_to_artist(target_id,source_ids):
|
||||
def associate_tracks_to_artist(target_id,source_ids,remove=False):
|
||||
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)
|
||||
if remove:
|
||||
log(f"Removing {sources} from {target}")
|
||||
sqldb.remove_artists_from_tracks(artist_ids=[target_id],track_ids=source_ids)
|
||||
else:
|
||||
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()
|
||||
@ -294,10 +302,14 @@ def associate_tracks_to_artist(target_id,source_ids):
|
||||
|
||||
@waitfordb
|
||||
def associate_tracks_to_album(target_id,source_ids):
|
||||
# target_id None means remove from current album!
|
||||
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})
|
||||
if target_id:
|
||||
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})
|
||||
else:
|
||||
sqldb.remove_album(source_ids)
|
||||
result = {'sources':sources,'target':target}
|
||||
dbcache.invalidate_entity_cache()
|
||||
dbcache.invalidate_caches()
|
||||
@ -350,7 +362,7 @@ def get_tracks(dbconn=None,**keys):
|
||||
def get_artists(dbconn=None):
|
||||
return sqldb.get_artists(dbconn=dbconn)
|
||||
|
||||
|
||||
@waitfordb
|
||||
def get_albums_artist_appears_on(dbconn=None,**keys):
|
||||
|
||||
artist_id = sqldb.get_artist_id(keys['artist'],dbconn=dbconn)
|
||||
|
@ -397,6 +397,14 @@ def add_tracks_to_albums(track_to_album_id_dict,replace=False,dbconn=None):
|
||||
for track_id in track_to_album_id_dict:
|
||||
add_track_to_album(track_id,track_to_album_id_dict[track_id],dbconn=dbconn)
|
||||
|
||||
@connection_provider
|
||||
def remove_album(*track_ids,dbconn=None):
|
||||
|
||||
DB['tracks'].update().where(
|
||||
DB['tracks'].c.track_id.in_(track_ids)
|
||||
).values(
|
||||
album_id=None
|
||||
)
|
||||
|
||||
### these will 'get' the ID of an entity, creating it if necessary
|
||||
|
||||
@ -640,6 +648,29 @@ def add_artists_to_tracks(track_ids,artist_ids,dbconn=None):
|
||||
|
||||
return True
|
||||
|
||||
@connection_provider
|
||||
def remove_artists_from_tracks(track_ids,artist_ids,dbconn=None):
|
||||
|
||||
# only tracks that have at least one other artist
|
||||
subquery = DB['trackartists'].select().where(
|
||||
~DB['trackartists'].c.artist_id.in_(artist_ids)
|
||||
).with_only_columns(
|
||||
DB['trackartists'].c.track_id
|
||||
).distinct().alias('sub')
|
||||
|
||||
op = DB['trackartists'].delete().where(
|
||||
sql.and_(
|
||||
DB['trackartists'].c.track_id.in_(track_ids),
|
||||
DB['trackartists'].c.artist_id.in_(artist_ids),
|
||||
DB['trackartists'].c.track_id.in_(subquery.select())
|
||||
)
|
||||
)
|
||||
|
||||
result = dbconn.execute(op)
|
||||
clean_db(dbconn=dbconn)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@connection_provider
|
||||
def add_artists_to_albums(album_ids,artist_ids,dbconn=None):
|
||||
@ -655,6 +686,22 @@ def add_artists_to_albums(album_ids,artist_ids,dbconn=None):
|
||||
return True
|
||||
|
||||
|
||||
@connection_provider
|
||||
def remove_artists_from_albums(album_ids,artist_ids,dbconn=None):
|
||||
|
||||
# no check here, albums are allowed to have zero artists
|
||||
|
||||
op = DB['albumartists'].delete().where(
|
||||
sql.and_(
|
||||
DB['albumartists'].c.album_id.in_(album_ids),
|
||||
DB['albumartists'].c.artist_id.in_(artist_ids)
|
||||
)
|
||||
)
|
||||
|
||||
result = dbconn.execute(op)
|
||||
clean_db(dbconn=dbconn)
|
||||
|
||||
return True
|
||||
|
||||
### Merge
|
||||
|
||||
|
@ -29,6 +29,8 @@
|
||||
|
||||
<div class="iconsubset associateicons" data-entity_type="album" data-entity_id="{{ info.id }}" data-entity_name="{{ info.album.albumtitle }}">
|
||||
{% include 'icons/add_album.jinja' %}
|
||||
<!-- no remove album since that is not a specified association - every track only has one album, so the removal should
|
||||
be handled on the track page (or for now, not at all) -->
|
||||
{% include 'icons/association_mark.jinja' %}
|
||||
{% include 'icons/association_unmark.jinja' %}
|
||||
{% include 'icons/association_cancel.jinja' %}
|
||||
|
@ -39,6 +39,7 @@
|
||||
|
||||
<div class="iconsubset associateicons" data-entity_type="artist" data-entity_id="{{ info.id }}" data-entity_name="{{ info.artist }}">
|
||||
{% include 'icons/add_artist.jinja' %}
|
||||
{% include 'icons/remove_artist.jinja' %}
|
||||
{% include 'icons/association_cancel.jinja' %}
|
||||
</div>
|
||||
|
||||
|
5
maloja/web/jinja/icons/disassociate.jinja
Normal file
5
maloja/web/jinja/icons/disassociate.jinja
Normal file
@ -0,0 +1,5 @@
|
||||
<div class='disassociateicon clickable_icon danger' onclick="disassociate(this)" title="Disassociate artists">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M20.347 3.653a3.936 3.936 0 0 0-5.567 0l-1.75 1.75a.75.75 0 0 1-1.06-1.06l1.75-1.75a5.436 5.436 0 0 1 7.688 7.687l-1.564 1.564a.75.75 0 0 1-1.06-1.06l1.563-1.564a3.936 3.936 0 0 0 0-5.567ZM9.786 12.369a.75.75 0 0 1 1.053.125c.096.122.2.24.314.353 1.348 1.348 3.386 1.587 4.89.658l-3.922-2.858a.745.745 0 0 1-.057-.037c-1.419-1.013-3.454-.787-4.784.543L3.653 14.78a3.936 3.936 0 0 0 5.567 5.567l3-3a.75.75 0 1 1 1.06 1.06l-3 3a5.436 5.436 0 1 1-7.688-7.687l3.628-3.628a5.517 5.517 0 0 1 3.014-1.547l-7.05-5.136a.75.75 0 0 1 .883-1.213l20.25 14.75a.75.75 0 0 1-.884 1.213l-5.109-3.722c-2.155 1.709-5.278 1.425-7.232-.53a5.491 5.491 0 0 1-.431-.485.75.75 0 0 1 .125-1.053Z"></path>
|
||||
</svg>
|
||||
</div>
|
6
maloja/web/jinja/icons/remove_album.jinja
Normal file
6
maloja/web/jinja/icons/remove_album.jinja
Normal file
@ -0,0 +1,6 @@
|
||||
<div title="Remove from Album" id="removealbumicon" class="clickable_icon" onclick="removeAssociate(this)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path d="M2 4.75C2 3.784 2.784 3 3.75 3h4.971a1.75 1.75 0 0 1 1.447.765l1.404 2.063a.25.25 0 0 0 .207.11h8.471c.966 0 1.75.783 1.75 1.75V19.25A1.75 1.75 0 0 1 20.25 21H4.75a.75.75 0 0 1 0-1.5h15.5a.25.25 0 0 0 .25-.25V7.688a.25.25 0 0 0-.25-.25h-8.471a1.751 1.751 0 0 1-1.447-.766L8.928 4.609a.252.252 0 0 0-.207-.109H3.75a.25.25 0 0 0-.25.25v3.5a.75.75 0 0 1-1.5 0v-3.5Z"></path>
|
||||
<path d="m 9.308 12.5 a 1 0.8 0 0 1 0 1.5 H 4.09 a 1 0.8 0 0 1 0 -1.5 h 5.218 Z"></path>
|
||||
</svg>
|
||||
</div>
|
5
maloja/web/jinja/icons/remove_artist.jinja
Normal file
5
maloja/web/jinja/icons/remove_artist.jinja
Normal file
@ -0,0 +1,5 @@
|
||||
<div title="Remove Artist" id="removeartisticon" class="clickable_icon" onclick="removeAssociate(this)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path d="M 4 9.5 a 5 5 0 1 1 7.916 4.062 a 7.973 7.973 0 0 1 5.018 7.166 a 0.75 0.75 0 1 1 -1.499 0.044 a 6.469 6.469 0 0 0 -12.932 0 a 0.75 0.75 0 0 1 -1.499 -0.044 a 7.972 7.972 0 0 1 5.059 -7.181 A 4.994 4.994 0 0 1 4 9.5 Z M 9 6 a 3.5 3.5 0 1 0 0 7 a 3.5 3.5 0 0 0 0 -7 Z M 20 4 h 2.25 a 0.75 0.75 0 0 1 0 1.5 H 20 h -1.5 h -2.25 a 0.75 0.75 0 0 1 0 -1.5 h 2.25 h 0.75 Z"></path>
|
||||
</svg>
|
||||
</div>
|
@ -162,7 +162,9 @@ we want icons to not be displayed in list rows, but show them with reduced opaci
|
||||
.mergeicons:not(.somethingmarked_for_merge) #mergeicon, /* can't merge when nothing is selected */
|
||||
.mergeicons.marked_for_merge #mergeicon, /* cant merge into one of the things we have selected */
|
||||
.associateicons:not(.sources_marked_for_associate) #associatealbumicon,
|
||||
.associateicons:not(.sources_marked_for_associate) #associateartisticon /* nothing marked yet, can't associate with this */
|
||||
.associateicons:not(.sources_marked_for_associate) #associateartisticon,
|
||||
.associateicons:not(.sources_marked_for_associate) #removealbumicon,
|
||||
.associateicons:not(.sources_marked_for_associate) #removeartisticon /* nothing marked yet, can't associate with this */
|
||||
{
|
||||
pointer-events: none;
|
||||
opacity:0.5;
|
||||
|
@ -453,6 +453,53 @@ function associate(element) {
|
||||
|
||||
}
|
||||
|
||||
function removeAssociate(element) {
|
||||
const parentElement = element.closest('[data-entity_id]');
|
||||
var entity_type = parentElement.dataset.entity_type;
|
||||
var entity_id = parentElement.dataset.entity_id;
|
||||
entity_id = parseInt(entity_id);
|
||||
|
||||
var requests_todo = 0;
|
||||
for (var target_entity_type of associate_sources[entity_type]) {
|
||||
var key = "marked_for_associate_" + target_entity_type;
|
||||
var current_stored = getStoredList(key);
|
||||
|
||||
if (current_stored.length != 0) {
|
||||
requests_todo += 1;
|
||||
callback_func = function(req){
|
||||
if (req.status == 200) {
|
||||
|
||||
toggleAssociationIcons(parentElement);
|
||||
notifyCallback(req);
|
||||
requests_todo -= 1;
|
||||
if (requests_todo == 0) {
|
||||
setTimeout(window.location.reload.bind(window.location),1000);
|
||||
}
|
||||
|
||||
}
|
||||
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,
|
||||
'remove': true
|
||||
},
|
||||
method="POST",
|
||||
callback=callback_func,
|
||||
json=true
|
||||
);
|
||||
|
||||
storeList(key,[]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function cancelMerge(element) {
|
||||
const parentElement = element.closest('[data-entity_id]');
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user