|
|
@ -39,25 +39,25 @@ import java.util.concurrent.ScheduledFuture; |
|
|
|
import java.util.concurrent.ThreadFactory; |
|
|
|
import java.util.concurrent.ThreadFactory; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import com.google.common.base.Predicate; |
|
|
|
|
|
|
|
import com.google.common.collect.Iterables; |
|
|
|
|
|
|
|
import com.google.common.collect.Lists; |
|
|
|
import org.apache.commons.io.FilenameUtils; |
|
|
|
import org.apache.commons.io.FilenameUtils; |
|
|
|
import org.apache.commons.io.IOUtils; |
|
|
|
import org.apache.commons.io.IOUtils; |
|
|
|
import org.apache.commons.lang.StringUtils; |
|
|
|
import org.apache.commons.lang.StringUtils; |
|
|
|
import org.apache.http.Header; |
|
|
|
import org.apache.http.Header; |
|
|
|
import org.apache.http.HttpResponse; |
|
|
|
import org.apache.http.HttpResponse; |
|
|
|
import org.apache.http.client.HttpClient; |
|
|
|
import org.apache.http.client.config.RequestConfig; |
|
|
|
|
|
|
|
import org.apache.http.client.methods.CloseableHttpResponse; |
|
|
|
import org.apache.http.client.methods.HttpGet; |
|
|
|
import org.apache.http.client.methods.HttpGet; |
|
|
|
import org.apache.http.entity.ContentType; |
|
|
|
import org.apache.http.entity.ContentType; |
|
|
|
import org.apache.http.impl.client.DefaultHttpClient; |
|
|
|
import org.apache.http.impl.client.CloseableHttpClient; |
|
|
|
import org.apache.http.params.HttpConnectionParams; |
|
|
|
import org.apache.http.impl.client.HttpClients; |
|
|
|
import org.jdom.Document; |
|
|
|
import org.jdom.Document; |
|
|
|
import org.jdom.Element; |
|
|
|
import org.jdom.Element; |
|
|
|
import org.jdom.Namespace; |
|
|
|
import org.jdom.Namespace; |
|
|
|
import org.jdom.input.SAXBuilder; |
|
|
|
import org.jdom.input.SAXBuilder; |
|
|
|
|
|
|
|
|
|
|
|
import com.google.common.base.Predicate; |
|
|
|
|
|
|
|
import com.google.common.collect.Iterables; |
|
|
|
|
|
|
|
import com.google.common.collect.Lists; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import org.libresonic.player.Logger; |
|
|
|
import org.libresonic.player.Logger; |
|
|
|
import org.libresonic.player.dao.PodcastDao; |
|
|
|
import org.libresonic.player.dao.PodcastDao; |
|
|
|
import org.libresonic.player.domain.MediaFile; |
|
|
|
import org.libresonic.player.domain.MediaFile; |
|
|
@ -302,33 +302,34 @@ public class PodcastService { |
|
|
|
@SuppressWarnings({"unchecked"}) |
|
|
|
@SuppressWarnings({"unchecked"}) |
|
|
|
private void doRefreshChannel(PodcastChannel channel, boolean downloadEpisodes) { |
|
|
|
private void doRefreshChannel(PodcastChannel channel, boolean downloadEpisodes) { |
|
|
|
InputStream in = null; |
|
|
|
InputStream in = null; |
|
|
|
HttpClient client = new DefaultHttpClient(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try (CloseableHttpClient client = HttpClients.createDefault()) { |
|
|
|
channel.setStatus(PodcastStatus.DOWNLOADING); |
|
|
|
channel.setStatus(PodcastStatus.DOWNLOADING); |
|
|
|
channel.setErrorMessage(null); |
|
|
|
channel.setErrorMessage(null); |
|
|
|
podcastDao.updateChannel(channel); |
|
|
|
podcastDao.updateChannel(channel); |
|
|
|
|
|
|
|
RequestConfig requestConfig = RequestConfig.custom() |
|
|
|
HttpConnectionParams.setConnectionTimeout(client.getParams(), 2 * 60 * 1000); // 2 minutes
|
|
|
|
.setConnectTimeout(2 * 60 * 1000) // 2 minutes
|
|
|
|
HttpConnectionParams.setSoTimeout(client.getParams(), 10 * 60 * 1000); // 10 minutes
|
|
|
|
.setSocketTimeout(10 * 60 * 1000) // 10 minutes
|
|
|
|
|
|
|
|
.build(); |
|
|
|
HttpGet method = new HttpGet(channel.getUrl()); |
|
|
|
HttpGet method = new HttpGet(channel.getUrl()); |
|
|
|
|
|
|
|
method.setConfig(requestConfig); |
|
|
|
|
|
|
|
|
|
|
|
HttpResponse response = client.execute(method); |
|
|
|
try (CloseableHttpResponse response = client.execute(method)) { |
|
|
|
in = response.getEntity().getContent(); |
|
|
|
in = response.getEntity().getContent(); |
|
|
|
|
|
|
|
|
|
|
|
Document document = new SAXBuilder().build(in); |
|
|
|
|
|
|
|
Element channelElement = document.getRootElement().getChild("channel"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
channel.setTitle(StringUtil.removeMarkup(channelElement.getChildTextTrim("title"))); |
|
|
|
Document document = new SAXBuilder().build(in); |
|
|
|
channel.setDescription(StringUtil.removeMarkup(channelElement.getChildTextTrim("description"))); |
|
|
|
Element channelElement = document.getRootElement().getChild("channel"); |
|
|
|
channel.setImageUrl(getChannelImageUrl(channelElement)); |
|
|
|
|
|
|
|
channel.setStatus(PodcastStatus.COMPLETED); |
|
|
|
|
|
|
|
channel.setErrorMessage(null); |
|
|
|
|
|
|
|
podcastDao.updateChannel(channel); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
downloadImage(channel); |
|
|
|
channel.setTitle(StringUtil.removeMarkup(channelElement.getChildTextTrim("title"))); |
|
|
|
refreshEpisodes(channel, channelElement.getChildren("item")); |
|
|
|
channel.setDescription(StringUtil.removeMarkup(channelElement.getChildTextTrim("description"))); |
|
|
|
|
|
|
|
channel.setImageUrl(getChannelImageUrl(channelElement)); |
|
|
|
|
|
|
|
channel.setStatus(PodcastStatus.COMPLETED); |
|
|
|
|
|
|
|
channel.setErrorMessage(null); |
|
|
|
|
|
|
|
podcastDao.updateChannel(channel); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
downloadImage(channel); |
|
|
|
|
|
|
|
refreshEpisodes(channel, channelElement.getChildren("item")); |
|
|
|
|
|
|
|
} |
|
|
|
} catch (Exception x) { |
|
|
|
} catch (Exception x) { |
|
|
|
LOG.warn("Failed to get/parse RSS file for Podcast channel " + channel.getUrl(), x); |
|
|
|
LOG.warn("Failed to get/parse RSS file for Podcast channel " + channel.getUrl(), x); |
|
|
|
channel.setStatus(PodcastStatus.ERROR); |
|
|
|
channel.setStatus(PodcastStatus.ERROR); |
|
|
@ -336,7 +337,6 @@ public class PodcastService { |
|
|
|
podcastDao.updateChannel(channel); |
|
|
|
podcastDao.updateChannel(channel); |
|
|
|
} finally { |
|
|
|
} finally { |
|
|
|
IOUtils.closeQuietly(in); |
|
|
|
IOUtils.closeQuietly(in); |
|
|
|
client.getConnectionManager().shutdown(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (downloadEpisodes) { |
|
|
|
if (downloadEpisodes) { |
|
|
@ -349,10 +349,9 @@ public class PodcastService { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void downloadImage(PodcastChannel channel) { |
|
|
|
private void downloadImage(PodcastChannel channel) { |
|
|
|
HttpClient client = new DefaultHttpClient(); |
|
|
|
|
|
|
|
InputStream in = null; |
|
|
|
InputStream in = null; |
|
|
|
OutputStream out = null; |
|
|
|
OutputStream out = null; |
|
|
|
try { |
|
|
|
try(CloseableHttpClient client = HttpClients.createDefault()) { |
|
|
|
String imageUrl = channel.getImageUrl(); |
|
|
|
String imageUrl = channel.getImageUrl(); |
|
|
|
if (imageUrl == null) { |
|
|
|
if (imageUrl == null) { |
|
|
|
return; |
|
|
|
return; |
|
|
@ -367,17 +366,17 @@ public class PodcastService { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
HttpGet method = new HttpGet(imageUrl); |
|
|
|
HttpGet method = new HttpGet(imageUrl); |
|
|
|
HttpResponse response = client.execute(method); |
|
|
|
try (CloseableHttpResponse response = client.execute(method)) { |
|
|
|
in = response.getEntity().getContent(); |
|
|
|
in = response.getEntity().getContent(); |
|
|
|
out = new FileOutputStream(new File(dir, "cover." + getCoverArtSuffix(response))); |
|
|
|
out = new FileOutputStream(new File(dir, "cover." + getCoverArtSuffix(response))); |
|
|
|
IOUtils.copy(in, out); |
|
|
|
IOUtils.copy(in, out); |
|
|
|
mediaFileService.refreshMediaFile(channelMediaFile); |
|
|
|
mediaFileService.refreshMediaFile(channelMediaFile); |
|
|
|
|
|
|
|
} |
|
|
|
} catch (Exception x) { |
|
|
|
} catch (Exception x) { |
|
|
|
LOG.warn("Failed to download cover art for podcast channel '" + channel.getTitle() + "': " + x, x); |
|
|
|
LOG.warn("Failed to download cover art for podcast channel '" + channel.getTitle() + "': " + x, x); |
|
|
|
} finally { |
|
|
|
} finally { |
|
|
|
IOUtils.closeQuietly(in); |
|
|
|
IOUtils.closeQuietly(in); |
|
|
|
IOUtils.closeQuietly(out); |
|
|
|
IOUtils.closeQuietly(out); |
|
|
|
client.getConnectionManager().shutdown(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -534,68 +533,69 @@ public class PodcastService { |
|
|
|
|
|
|
|
|
|
|
|
LOG.info("Starting to download Podcast from " + episode.getUrl()); |
|
|
|
LOG.info("Starting to download Podcast from " + episode.getUrl()); |
|
|
|
|
|
|
|
|
|
|
|
HttpClient client = new DefaultHttpClient(); |
|
|
|
try (CloseableHttpClient client = HttpClients.createDefault()) { |
|
|
|
try { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!settingsService.getLicenseInfo().isLicenseOrTrialValid()) { |
|
|
|
if (!settingsService.getLicenseInfo().isLicenseOrTrialValid()) { |
|
|
|
throw new Exception("Sorry, the trial period is expired."); |
|
|
|
throw new Exception("Sorry, the trial period is expired."); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
PodcastChannel channel = getChannel(episode.getChannelId()); |
|
|
|
PodcastChannel channel = getChannel(episode.getChannelId()); |
|
|
|
|
|
|
|
RequestConfig requestConfig = RequestConfig.custom() |
|
|
|
HttpConnectionParams.setConnectionTimeout(client.getParams(), 2 * 60 * 1000); // 2 minutes
|
|
|
|
.setConnectTimeout(2 * 60 * 1000) // 2 minutes
|
|
|
|
HttpConnectionParams.setSoTimeout(client.getParams(), 10 * 60 * 1000); // 10 minutes
|
|
|
|
.setSocketTimeout(10 * 60 * 1000) // 10 minutes
|
|
|
|
|
|
|
|
.build(); |
|
|
|
HttpGet method = new HttpGet(episode.getUrl()); |
|
|
|
HttpGet method = new HttpGet(episode.getUrl()); |
|
|
|
|
|
|
|
method.setConfig(requestConfig); |
|
|
|
|
|
|
|
|
|
|
|
HttpResponse response = client.execute(method); |
|
|
|
try (CloseableHttpResponse response = client.execute(method)) { |
|
|
|
in = response.getEntity().getContent(); |
|
|
|
in = response.getEntity().getContent(); |
|
|
|
|
|
|
|
|
|
|
|
File file = getFile(channel, episode); |
|
|
|
File file = getFile(channel, episode); |
|
|
|
out = new FileOutputStream(file); |
|
|
|
out = new FileOutputStream(file); |
|
|
|
|
|
|
|
|
|
|
|
episode.setStatus(PodcastStatus.DOWNLOADING); |
|
|
|
episode.setStatus(PodcastStatus.DOWNLOADING); |
|
|
|
episode.setBytesDownloaded(0L); |
|
|
|
episode.setBytesDownloaded(0L); |
|
|
|
episode.setErrorMessage(null); |
|
|
|
episode.setErrorMessage(null); |
|
|
|
episode.setPath(file.getPath()); |
|
|
|
episode.setPath(file.getPath()); |
|
|
|
podcastDao.updateEpisode(episode); |
|
|
|
podcastDao.updateEpisode(episode); |
|
|
|
|
|
|
|
|
|
|
|
byte[] buffer = new byte[4096]; |
|
|
|
byte[] buffer = new byte[4096]; |
|
|
|
long bytesDownloaded = 0; |
|
|
|
long bytesDownloaded = 0; |
|
|
|
int n; |
|
|
|
int n; |
|
|
|
long nextLogCount = 30000L; |
|
|
|
long nextLogCount = 30000L; |
|
|
|
|
|
|
|
|
|
|
|
while ((n = in.read(buffer)) != -1) { |
|
|
|
while ((n = in.read(buffer)) != -1) { |
|
|
|
out.write(buffer, 0, n); |
|
|
|
out.write(buffer, 0, n); |
|
|
|
bytesDownloaded += n; |
|
|
|
bytesDownloaded += n; |
|
|
|
|
|
|
|
|
|
|
|
if (bytesDownloaded > nextLogCount) { |
|
|
|
if (bytesDownloaded > nextLogCount) { |
|
|
|
episode.setBytesDownloaded(bytesDownloaded); |
|
|
|
episode.setBytesDownloaded(bytesDownloaded); |
|
|
|
nextLogCount += 30000L; |
|
|
|
nextLogCount += 30000L; |
|
|
|
|
|
|
|
|
|
|
|
// Abort download if episode was deleted by user.
|
|
|
|
// Abort download if episode was deleted by user.
|
|
|
|
if (isEpisodeDeleted(episode)) { |
|
|
|
if (isEpisodeDeleted(episode)) { |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
podcastDao.updateEpisode(episode); |
|
|
|
} |
|
|
|
} |
|
|
|
podcastDao.updateEpisode(episode); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (isEpisodeDeleted(episode)) { |
|
|
|
if (isEpisodeDeleted(episode)) { |
|
|
|
LOG.info("Podcast " + episode.getUrl() + " was deleted. Aborting download."); |
|
|
|
LOG.info("Podcast " + episode.getUrl() + " was deleted. Aborting download."); |
|
|
|
IOUtils.closeQuietly(out); |
|
|
|
IOUtils.closeQuietly(out); |
|
|
|
file.delete(); |
|
|
|
file.delete(); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
addMediaFileIdToEpisodes(Arrays.asList(episode)); |
|
|
|
addMediaFileIdToEpisodes(Arrays.asList(episode)); |
|
|
|
episode.setBytesDownloaded(bytesDownloaded); |
|
|
|
episode.setBytesDownloaded(bytesDownloaded); |
|
|
|
podcastDao.updateEpisode(episode); |
|
|
|
podcastDao.updateEpisode(episode); |
|
|
|
LOG.info("Downloaded " + bytesDownloaded + " bytes from Podcast " + episode.getUrl()); |
|
|
|
LOG.info("Downloaded " + bytesDownloaded + " bytes from Podcast " + episode.getUrl()); |
|
|
|
IOUtils.closeQuietly(out); |
|
|
|
IOUtils.closeQuietly(out); |
|
|
|
updateTags(file, episode); |
|
|
|
updateTags(file, episode); |
|
|
|
episode.setStatus(PodcastStatus.COMPLETED); |
|
|
|
episode.setStatus(PodcastStatus.COMPLETED); |
|
|
|
podcastDao.updateEpisode(episode); |
|
|
|
podcastDao.updateEpisode(episode); |
|
|
|
deleteObsoleteEpisodes(channel); |
|
|
|
deleteObsoleteEpisodes(channel); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception x) { |
|
|
|
} catch (Exception x) { |
|
|
|
LOG.warn("Failed to download Podcast from " + episode.getUrl(), x); |
|
|
|
LOG.warn("Failed to download Podcast from " + episode.getUrl(), x); |
|
|
|
episode.setStatus(PodcastStatus.ERROR); |
|
|
|
episode.setStatus(PodcastStatus.ERROR); |
|
|
@ -604,7 +604,6 @@ public class PodcastService { |
|
|
|
} finally { |
|
|
|
} finally { |
|
|
|
IOUtils.closeQuietly(in); |
|
|
|
IOUtils.closeQuietly(in); |
|
|
|
IOUtils.closeQuietly(out); |
|
|
|
IOUtils.closeQuietly(out); |
|
|
|
client.getConnectionManager().shutdown(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|