595 lines
17 KiB
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());
|
|
}
|
|
}
|
|
}
|