Audinaut-subsonic-app-android/app/src/main/java/net/nullsum/audinaut/domain/MusicDirectory.java

595 lines
17 KiB
Java

/*
This file is part of Subsonic.
Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009 (C) Sindre Mehus
*/
package net.nullsum.audinaut.domain;
import android.content.Context;
import android.media.MediaMetadataRetriever;
import android.util.Log;
import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.UpdateHelper;
import net.nullsum.audinaut.util.Util;
import java.io.File;
import java.io.Serializable;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
/**
* @author Sindre Mehus
*/
public class MusicDirectory implements Serializable {
private static final String TAG = MusicDirectory.class.getSimpleName();
private String name;
private String id;
private String parent;
private List<Entry> children;
public MusicDirectory() {
children = new ArrayList<>();
}
public MusicDirectory(List<Entry> children) {
this.children = children;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
public void addChild(Entry child) {
if (child != null) {
children.add(child);
}
}
public void addChildren(List<Entry> children) {
this.children.addAll(children);
}
public void replaceChildren(List<Entry> children) {
this.children = children;
}
public synchronized List<Entry> getChildren() {
return getChildren(true, true);
}
public synchronized List<Entry> getChildren(boolean includeDirs, boolean includeFiles) {
if (includeDirs && includeFiles) {
return children;
}
List<Entry> result = new ArrayList<>(children.size());
for (Entry child : children) {
if (child != null && child.isDirectory() && includeDirs || !child.isDirectory() && includeFiles) {
result.add(child);
}
}
return result;
}
public synchronized List<Entry> getSongs() {
List<Entry> result = new ArrayList<>();
for (Entry child : children) {
if (child != null && !child.isDirectory()) {
result.add(child);
}
}
return result;
}
public synchronized int getChildrenSize() {
return children.size();
}
public void sortChildren(Context context) {
sortChildren(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_CUSTOM_SORT_ENABLED, true));
}
public void sortChildren(boolean byYear) {
EntryComparator.sort(children, byYear);
}
public synchronized boolean updateMetadata(MusicDirectory refreshedDirectory) {
boolean metadataUpdated = false;
for (Entry entry : children) {
int index = refreshedDirectory.children.indexOf(entry);
if (index != -1) {
final Entry refreshed = refreshedDirectory.children.get(index);
entry.setTitle(refreshed.getTitle());
entry.setAlbum(refreshed.getAlbum());
entry.setArtist(refreshed.getArtist());
entry.setTrack(refreshed.getTrack());
entry.setYear(refreshed.getYear());
entry.setGenre(refreshed.getGenre());
entry.setTranscodedContentType(refreshed.getTranscodedContentType());
entry.setTranscodedSuffix(refreshed.getTranscodedSuffix());
entry.setDiscNumber(refreshed.getDiscNumber());
entry.setType(refreshed.getType());
if (!Util.equals(entry.getCoverArt(), refreshed.getCoverArt())) {
metadataUpdated = true;
entry.setCoverArt(refreshed.getCoverArt());
}
new UpdateHelper.EntryInstanceUpdater(entry) {
@Override
public void update(Entry found) {
found.setTitle(refreshed.getTitle());
found.setAlbum(refreshed.getAlbum());
found.setArtist(refreshed.getArtist());
found.setTrack(refreshed.getTrack());
found.setYear(refreshed.getYear());
found.setGenre(refreshed.getGenre());
found.setTranscodedContentType(refreshed.getTranscodedContentType());
found.setTranscodedSuffix(refreshed.getTranscodedSuffix());
found.setDiscNumber(refreshed.getDiscNumber());
found.setType(refreshed.getType());
if (!Util.equals(found.getCoverArt(), refreshed.getCoverArt())) {
found.setCoverArt(refreshed.getCoverArt());
metadataUpdate = DownloadService.METADATA_UPDATED_COVER_ART;
}
}
}.execute();
}
}
return metadataUpdated;
}
public synchronized boolean updateEntriesList(Context context, MusicDirectory refreshedDirectory) {
boolean changed = false;
Iterator<Entry> it = children.iterator();
while (it.hasNext()) {
Entry entry = it.next();
// No longer exists in here
if (refreshedDirectory.children.indexOf(entry) == -1) {
it.remove();
changed = true;
}
}
// Make sure we contain all children from refreshed set
boolean resort = false;
for (Entry refreshed : refreshedDirectory.children) {
if (!this.children.contains(refreshed)) {
this.children.add(refreshed);
resort = true;
changed = true;
}
}
if (resort) {
this.sortChildren(context);
}
return changed;
}
public static class Entry implements Serializable {
public static final int TYPE_SONG = 0;
private String id;
private String parent;
private String albumId;
private String artistId;
private boolean directory;
private String title;
private String album;
private String artist;
private Integer track;
private Integer year;
private String genre;
private String contentType;
private String suffix;
private String transcodedContentType;
private String transcodedSuffix;
private String coverArt;
private Integer duration;
private Integer bitRate;
private String path;
private Integer discNumber;
private int type = 0;
private int closeness;
public Entry() {
}
public Entry(String id) {
this.id = id;
}
public Entry(Artist artist) {
this.id = artist.getId();
this.title = artist.getName();
this.directory = true;
}
public void loadMetadata(File file) {
try {
MediaMetadataRetriever metadata = new MediaMetadataRetriever();
metadata.setDataSource(file.getAbsolutePath());
String discNumber = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER);
if (discNumber == null) {
discNumber = "1/1";
}
int slashIndex = discNumber.indexOf("/");
if (slashIndex > 0) {
discNumber = discNumber.substring(0, slashIndex);
}
try {
setDiscNumber(Integer.parseInt(discNumber));
} catch (Exception e) {
Log.w(TAG, "Non numbers in disc field!");
}
String bitrate = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE);
setBitRate(Integer.parseInt((bitrate != null) ? bitrate : "0") / 1000);
String length = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
setDuration(Integer.parseInt(length) / 1000);
String artist = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
if (artist != null) {
setArtist(artist);
}
String album = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM);
if (album != null) {
setAlbum(album);
}
metadata.release();
} catch (Exception e) {
Log.i(TAG, "Device doesn't properly support MediaMetadataRetreiver", e);
}
}
public void rebaseTitleOffPath() {
try {
String filename = getPath();
if (filename == null) {
return;
}
int index = filename.lastIndexOf('/');
if (index != -1) {
filename = filename.substring(index + 1);
if (getTrack() != null) {
filename = filename.replace(String.format("%02d ", getTrack()), "");
}
index = filename.lastIndexOf('.');
if (index != -1) {
filename = filename.substring(0, index);
}
setTitle(filename);
}
} catch (Exception e) {
Log.w(TAG, "Failed to update title based off of path", e);
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
public String getAlbumId() {
return albumId;
}
public void setAlbumId(String albumId) {
this.albumId = albumId;
}
public String getArtistId() {
return artistId;
}
public void setArtistId(String artistId) {
this.artistId = artistId;
}
public boolean isDirectory() {
return directory;
}
public void setDirectory(boolean directory) {
this.directory = directory;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAlbum() {
return album;
}
public boolean isAlbum() {
return getParent() != null || getArtist() != null;
}
public void setAlbum(String album) {
this.album = album;
}
public String getAlbumDisplay() {
if (album != null && title.startsWith("Disc ")) {
return album;
} else {
return title;
}
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public Integer getTrack() {
return track;
}
public void setTrack(Integer track) {
this.track = track;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public String getTranscodedContentType() {
return transcodedContentType;
}
public void setTranscodedContentType(String transcodedContentType) {
this.transcodedContentType = transcodedContentType;
}
public String getTranscodedSuffix() {
return transcodedSuffix;
}
public void setTranscodedSuffix(String transcodedSuffix) {
this.transcodedSuffix = transcodedSuffix;
}
public Integer getDuration() {
return duration;
}
public void setDuration(Integer duration) {
this.duration = duration;
}
public Integer getBitRate() {
return bitRate;
}
public void setBitRate(Integer bitRate) {
this.bitRate = bitRate;
}
public String getCoverArt() {
return coverArt;
}
public void setCoverArt(String coverArt) {
this.coverArt = coverArt;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Integer getDiscNumber() {
return discNumber;
}
public void setDiscNumber(Integer discNumber) {
this.discNumber = discNumber;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public boolean isSong() {
return type == TYPE_SONG;
}
public int getCloseness() {
return closeness;
}
public void setCloseness(int closeness) {
this.closeness = closeness;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Entry entry = (Entry) o;
return id.equals(entry.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public String toString() {
return title;
}
}
public static class EntryComparator implements Comparator<Entry> {
private final boolean byYear;
private final Collator collator;
public EntryComparator(boolean byYear) {
this.byYear = byYear;
this.collator = Collator.getInstance(Locale.US);
this.collator.setStrength(Collator.PRIMARY);
}
public static void sort(List<Entry> entries, boolean byYear) {
try {
Collections.sort(entries, new EntryComparator(byYear));
} catch (Exception e) {
Log.w(TAG, "Failed to sort MusicDirectory");
}
}
public int compare(Entry lhs, Entry rhs) {
if (lhs.isDirectory() && !rhs.isDirectory()) {
return -1;
} else if (!lhs.isDirectory() && rhs.isDirectory()) {
return 1;
} else if (lhs.isDirectory() && rhs.isDirectory()) {
if (byYear) {
Integer lhsYear = lhs.getYear();
Integer rhsYear = rhs.getYear();
if (lhsYear != null && rhsYear != null) {
return lhsYear.compareTo(rhsYear);
} else if (lhsYear != null) {
return -1;
} else if (rhsYear != null) {
return 1;
}
}
return collator.compare(lhs.getAlbumDisplay(), rhs.getAlbumDisplay());
}
Integer lhsDisc = lhs.getDiscNumber();
Integer rhsDisc = rhs.getDiscNumber();
if (lhsDisc != null && rhsDisc != null) {
if (lhsDisc < rhsDisc) {
return -1;
} else if (lhsDisc > rhsDisc) {
return 1;
}
}
Integer lhsTrack = lhs.getTrack();
Integer rhsTrack = rhs.getTrack();
if (lhsTrack != null && rhsTrack != null && !Objects.equals(lhsTrack, rhsTrack)) {
return lhsTrack.compareTo(rhsTrack);
} else if (lhsTrack != null) {
return -1;
} else if (rhsTrack != null) {
return 1;
}
return collator.compare(lhs.getTitle(), rhs.getTitle());
}
}
}