/*
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 2014 (C) Scott Jackson
*/
package github.daneren2005.dsub.util;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
public class ArtistRadioBuffer {
private static final String TAG = ArtistRadioBuffer.class.getSimpleName();
private ScheduledExecutorService executorService;
private Runnable runnable;
private final ArrayList<MusicDirectory.Entry> buffer = new ArrayList<MusicDirectory.Entry>();
private int lastCount = -1;
private DownloadService context;
private boolean awaitingResults = false;
private int capacity;
private int refillThreshold;
private String artistId;
public ArtistRadioBuffer(DownloadService context) {
this.context = context;
runnable = new Runnable() {
@Override
public void run() {
refill();
}
};
// Calculate out the capacity and refill threshold based on the user's random size preference
int shuffleListSize = Math.max(1, Integer.parseInt(Util.getPreferences(context).getString(Constants.PREFERENCES_KEY_RANDOM_SIZE, "20")));
// ex: default 20 -> 50
capacity = shuffleListSize * 5 / 2;
capacity = Math.min(500, capacity);
// ex: default 20 -> 40
refillThreshold = capacity * 4 / 5;
}
public void setArtist(String artistId) {
if(!Util.equals(this.artistId, artistId)) {
buffer.clear();
}
this.artistId = artistId;
awaitingResults = true;
refill();
}
public void restoreArtist(String artistId) {
this.artistId = artistId;
awaitingResults = false;
restart();
}
public List<MusicDirectory.Entry> get(int size) {
// Make sure fetcher is running if needed
restart();
List<MusicDirectory.Entry> result = new ArrayList<MusicDirectory.Entry>(size);
synchronized (buffer) {
while (!buffer.isEmpty() && result.size() < size) {
result.add(buffer.remove(buffer.size() - 1));
}
}
Log.i(TAG, "Taking " + result.size() + " songs from artist radio buffer. " + buffer.size() + " remaining.");
if(result.isEmpty()) {
awaitingResults = true;
}
return result;
}
public void shutdown() {
executorService.shutdown();
}
private void restart() {
synchronized(buffer) {
if(buffer.size() <= refillThreshold && lastCount != 0 && (executorService == null || executorService.isShutdown())) {
executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleWithFixedDelay(runnable, 0, 10, TimeUnit.SECONDS);
}
}
}
private void refill() {
if (buffer != null && executorService != null && (buffer.size() > refillThreshold || (!Util.isNetworkConnected(context) && !Util.isOffline(context)) || lastCount == 0)) {
executorService.shutdown();
return;
}
try {
MusicService service = MusicServiceFactory.getMusicService(context);
// Get capacity based
int n = capacity - buffer.size();
MusicDirectory songs = service.getRandomSongs(n, artistId, context, null);
synchronized (buffer) {
lastCount = 0;
for(MusicDirectory.Entry entry: songs.getChildren()) {
if(!buffer.contains(entry) && entry.getRating() != 1) {
buffer.add(entry);
lastCount++;
}
}
Log.i(TAG, "Refilled artist radio buffer with " + lastCount + " songs.");
}
} catch (Exception x) {
// Give it one more try before quitting
if(lastCount != -2) {
lastCount = -2;
} else if(lastCount == -2) {
lastCount = 0;
}
Log.w(TAG, "Failed to refill artist radio buffer.", x);
}
if(awaitingResults) {
awaitingResults = false;
context.checkDownloads();
}
}
}