public synchronized void schedule() { Runnable task = new Runnable() { public void run() { LOG.info("Starting scheduled Podcast refresh."); refreshAllChannels(true); LOG.info("Completed scheduled Podcast refresh."); } }; if (scheduledRefresh != null) { scheduledRefresh.cancel(true); } int hoursBetween = settingsService.getPodcastUpdateInterval(); if (hoursBetween == -1) { LOG.info("Automatic Podcast update disabled."); return; } long periodMillis = hoursBetween * 60L * 60L * 1000L; long initialDelayMillis = 5L * 60L * 1000L; scheduledRefresh = scheduledExecutor.scheduleAtFixedRate( task, initialDelayMillis, periodMillis, TimeUnit.MILLISECONDS); Date firstTime = new Date(System.currentTimeMillis() + initialDelayMillis); LOG.info( "Automatic Podcast update scheduled to run every " + hoursBetween + " hour(s), starting at " + firstTime); }
public void execute(JdbcTemplate template) { if (template.queryForInt("select count(*) from version where version = 6") == 0) { LOG.info("Updating database schema to version 6."); template.execute("insert into version values (6)"); } if (!columnExists(template, "last_fm_enabled", "user_settings")) { LOG.info("Database columns 'user_settings.last_fm_*' not found. Creating them."); template.execute( "alter table user_settings add last_fm_enabled boolean default false not null"); template.execute("alter table user_settings add last_fm_username varchar null"); template.execute("alter table user_settings add last_fm_password varchar null"); LOG.info("Database columns 'user_settings.last_fm_*' were added successfully."); } if (!columnExists(template, "transcode_scheme", "user_settings")) { LOG.info("Database column 'user_settings.transcode_scheme' not found. Creating it."); template.execute( "alter table user_settings add transcode_scheme varchar default '" + TranscodeScheme.OFF.name() + "' not null"); LOG.info("Database column 'user_settings.transcode_scheme' was added successfully."); } }
@Override public void execute(JdbcTemplate template) { if (template.queryForInt("select count(*) from version where version = 21") == 0) { LOG.info("Updating database schema to version 21."); template.execute("insert into version values (21)"); } if (!columnExists(template, "year", "album")) { LOG.info("Database column 'album.year' not found. Creating it."); template.execute("alter table album add year int"); LOG.info("Database column 'album.year' was added successfully."); } if (!columnExists(template, "genre", "album")) { LOG.info("Database column 'album.genre' not found. Creating it."); template.execute("alter table album add genre varchar"); LOG.info("Database column 'album.genre' was added successfully."); } if (!tableExists(template, "genre")) { LOG.info("Database table 'genre' not found. Creating it."); template.execute( "create table genre (" + "name varchar not null," + "song_count int not null)"); LOG.info("Database table 'genre' was created successfully."); } if (!columnExists(template, "album_count", "genre")) { LOG.info("Database column 'genre.album_count' not found. Creating it."); template.execute("alter table genre add album_count int default 0 not null"); LOG.info("Database column 'genre.album_count' was added successfully."); } }
@Override public void execute(JdbcTemplate template) { if (template.queryForInt("select count(*) from version where version = 28") == 0) { LOG.info("Updating database schema to version 28."); template.execute("insert into version values (28)"); if (!columnExists(template, "only_album_artist_recommendations", "user_settings")) { LOG.info( "Database column 'user_settings.only_album_artist_recommendations' not found. Creating it."); template.execute( "alter table user_settings add only_album_artist_recommendations boolean default true not null"); LOG.info( "Database column 'user_settings.only_album_artist_recommendations' was added successfully."); } } }
public void execute(JdbcTemplate template) { if (template.queryForInt("select count(*) from version where version = 10") == 0) { LOG.info("Updating database schema to version 10."); template.execute("insert into version values (10)"); } if (!columnExists(template, "ldap_authenticated", "users")) { LOG.info("Database column 'users.ldap_authenticated' not found. Creating it."); template.execute("alter table users add ldap_authenticated boolean default false not null"); LOG.info("Database column 'users.ldap_authenticated' was added successfully."); } if (!columnExists(template, "party_mode_enabled", "users_settings")) { LOG.info("Database column 'users_settings.party_mode_enabled' not found. Creating it."); template.execute( "alter table users_settings add party_mode_enabled boolean default false not null"); LOG.info("Database column 'users_settings.party_mode_enabled' was added successfully."); } }
private void saveCoverArt(String path, String url) throws Exception { InputStream input = null; HttpClient client = new DefaultHttpClient(); try { HttpConnectionParams.setConnectionTimeout(client.getParams(), 20 * 1000); // 20 seconds HttpConnectionParams.setSoTimeout(client.getParams(), 20 * 1000); // 20 seconds HttpGet method = new HttpGet(url); HttpResponse response = client.execute(method); input = response.getEntity().getContent(); // Attempt to resolve proper suffix. String suffix = "jpg"; if (url.toLowerCase().endsWith(".gif")) { suffix = "gif"; } else if (url.toLowerCase().endsWith(".png")) { suffix = "png"; } // Check permissions. File newCoverFile = new File(path, "cover." + suffix); if (!securityService.isWriteAllowed(newCoverFile)) { throw new Exception("Permission denied: " + StringUtil.toHtml(newCoverFile.getPath())); } // If file exists, create a backup. backup(newCoverFile, new File(path, "cover.backup." + suffix)); // Write file. IOUtils.copy(input, new FileOutputStream(newCoverFile)); MediaFile mediaFile = mediaFileService.getMediaFile(path); // Rename existing cover file if new cover file is not the preferred. try { File coverFile = mediaFileService.getCoverArt(mediaFile); if (coverFile != null) { if (!newCoverFile.equals(coverFile)) { coverFile.renameTo(new File(coverFile.getCanonicalPath() + ".old")); LOG.info("Renamed old image file " + coverFile); } } } catch (Exception x) { LOG.warn("Failed to rename existing cover file.", x); } mediaFileService.refreshMediaFile(mediaFile); } finally { IOUtils.closeQuietly(input); client.getConnectionManager().shutdown(); } }
public void execute(JdbcTemplate template) { if (!tableExists(template, "version")) { LOG.info("Database table 'version' not found. Creating it."); template.execute("create table version (version int not null)"); template.execute("insert into version values (1)"); LOG.info("Database table 'version' was created successfully."); } if (!tableExists(template, "role")) { LOG.info("Database table 'role' not found. Creating it."); template.execute( "create table role (" + "id int not null," + "name varchar not null," + "primary key (id))"); template.execute("insert into role values (1, 'admin')"); template.execute("insert into role values (2, 'download')"); template.execute("insert into role values (3, 'upload')"); template.execute("insert into role values (4, 'playlist')"); template.execute("insert into role values (5, 'coverart')"); LOG.info("Database table 'role' was created successfully."); } if (!tableExists(template, "user")) { LOG.info("Database table 'user' not found. Creating it."); template.execute( "create table user (" + "username varchar not null," + "password varchar not null," + "primary key (username))"); template.execute("insert into user values ('admin', 'admin')"); LOG.info("Database table 'user' was created successfully."); } if (!tableExists(template, "user_role")) { LOG.info("Database table 'user_role' not found. Creating it."); template.execute( "create table user_role (" + "username varchar not null," + "role_id int not null," + "primary key (username, role_id)," + "foreign key (username) references user(username)," + "foreign key (role_id) references role(id))"); template.execute("insert into user_role values ('admin', 1)"); template.execute("insert into user_role values ('admin', 2)"); template.execute("insert into user_role values ('admin', 3)"); template.execute("insert into user_role values ('admin', 4)"); template.execute("insert into user_role values ('admin', 5)"); LOG.info("Database table 'user_role' was created successfully."); } }
private void backup(File newCoverFile, File backup) { if (newCoverFile.exists()) { if (backup.exists()) { backup.delete(); } if (newCoverFile.renameTo(backup)) { LOG.info("Backed up old image file to " + backup); } else { LOG.warn("Failed to create image file backup " + backup); } } }
private synchronized File getImageCacheDirectory(int size) { File dir = new File(SettingsService.getSubsonicHome(), "thumbs"); dir = new File(dir, String.valueOf(size)); if (!dir.exists()) { if (dir.mkdirs()) { LOG.info("Created thumbnail cache " + dir); } else { LOG.error("Failed to create thumbnail cache " + dir); } } return dir; }
public void execute(JdbcTemplate template) { if (template.queryForInt("select count(*) from version where version = 3") == 0) { LOG.info("Updating database schema to version 3."); template.execute("insert into version values (3)"); LOG.info("Converting database column 'music_file_info.path' to varchar_ignorecase."); template.execute("drop index idx_music_file_info_path"); template.execute("alter table music_file_info alter column path varchar_ignorecase not null"); template.execute("create index idx_music_file_info_path on music_file_info(path)"); LOG.info("Database column 'music_file_info.path' was converted successfully."); } if (!columnExists(template, "bytes_streamed", "user")) { LOG.info( "Database columns 'user.bytes_streamed/downloaded/uploaded' not found. Creating them."); template.execute("alter table user add bytes_streamed bigint default 0 not null"); template.execute("alter table user add bytes_downloaded bigint default 0 not null"); template.execute("alter table user add bytes_uploaded bigint default 0 not null"); LOG.info( "Database columns 'user.bytes_streamed/downloaded/uploaded' were added successfully."); } }
public synchronized void init() { // Clean up partial downloads. for (PodcastChannel channel : getAllChannels()) { for (PodcastEpisode episode : getEpisodes(channel.getId(), false)) { if (episode.getStatus() == PodcastStatus.DOWNLOADING) { deleteEpisode(episode.getId(), false); LOG.info( "Deleted Podcast episode '" + episode.getTitle() + "' since download was interrupted."); } } } schedule(); }
private void removeLocks() { for (IndexType indexType : IndexType.values()) { Directory dir = null; try { dir = FSDirectory.open(getIndexDirectory(indexType)); if (IndexWriter.isLocked(dir)) { IndexWriter.unlock(dir); LOG.info("Removed Lucene lock file in " + dir); } } catch (Exception x) { LOG.warn("Failed to remove Lucene lock file in " + dir, x); } finally { FileUtil.closeQuietly(dir); } } }
private synchronized void deleteObsoleteEpisodes(PodcastChannel channel) { int episodeCount = settingsService.getPodcastEpisodeRetentionCount(); if (episodeCount == -1) { return; } List<PodcastEpisode> episodes = getEpisodes(channel.getId(), false); // Don't do anything if other episodes of the same channel is currently downloading. for (PodcastEpisode episode : episodes) { if (episode.getStatus() == PodcastStatus.DOWNLOADING) { return; } } // Reverse array to get chronological order (oldest episodes first). Collections.reverse(episodes); int episodesToDelete = Math.max(0, episodes.size() - episodeCount); for (int i = 0; i < episodesToDelete; i++) { deleteEpisode(episodes.get(i).getId(), true); LOG.info("Deleted old Podcast episode " + episodes.get(i).getUrl()); } }
private void doDownloadEpisode(PodcastEpisode episode) { InputStream in = null; OutputStream out = null; if (getEpisode(episode.getId(), false) == null) { LOG.info("Podcast " + episode.getUrl() + " was deleted. Aborting download."); return; } LOG.info("Starting to download Podcast from " + episode.getUrl()); HttpClient client = new DefaultHttpClient(); try { PodcastChannel channel = getChannel(episode.getChannelId()); HttpConnectionParams.setConnectionTimeout(client.getParams(), 2 * 60 * 1000); // 2 minutes HttpConnectionParams.setSoTimeout(client.getParams(), 10 * 60 * 1000); // 10 minutes HttpGet method = new HttpGet(episode.getUrl()); HttpResponse response = client.execute(method); in = response.getEntity().getContent(); File file = getFile(channel, episode); out = new FileOutputStream(file); episode.setStatus(PodcastStatus.DOWNLOADING); episode.setBytesDownloaded(0L); episode.setErrorMessage(null); episode.setPath(file.getPath()); podcastDao.updateEpisode(episode); byte[] buffer = new byte[4096]; long bytesDownloaded = 0; int n; long nextLogCount = 30000L; while ((n = in.read(buffer)) != -1) { out.write(buffer, 0, n); bytesDownloaded += n; if (bytesDownloaded > nextLogCount) { episode.setBytesDownloaded(bytesDownloaded); nextLogCount += 30000L; if (getEpisode(episode.getId(), false) == null) { break; } podcastDao.updateEpisode(episode); } } if (getEpisode(episode.getId(), false) == null) { LOG.info("Podcast " + episode.getUrl() + " was deleted. Aborting download."); IOUtils.closeQuietly(out); file.delete(); } else { episode.setBytesDownloaded(bytesDownloaded); podcastDao.updateEpisode(episode); LOG.info("Downloaded " + bytesDownloaded + " bytes from Podcast " + episode.getUrl()); IOUtils.closeQuietly(out); episode.setStatus(PodcastStatus.COMPLETED); podcastDao.updateEpisode(episode); deleteObsoleteEpisodes(channel); } } catch (Exception x) { LOG.warn("Failed to download Podcast from " + episode.getUrl(), x); episode.setStatus(PodcastStatus.ERROR); episode.setErrorMessage(x.toString()); podcastDao.updateEpisode(episode); } finally { IOUtils.closeQuietly(in); IOUtils.closeQuietly(out); client.getConnectionManager().shutdown(); } }
private void refreshEpisodes(PodcastChannel channel, List<Element> episodeElements) { List<PodcastEpisode> episodes = new ArrayList<PodcastEpisode>(); for (Element episodeElement : episodeElements) { String title = episodeElement.getChildTextTrim("title"); String duration = getITunesElement(episodeElement, "duration"); String description = episodeElement.getChildTextTrim("description"); if (StringUtils.isBlank(description)) { description = getITunesElement(episodeElement, "summary"); } Element enclosure = episodeElement.getChild("enclosure"); if (enclosure == null) { LOG.debug("No enclosure found for episode " + title); continue; } String url = enclosure.getAttributeValue("url"); url = sanitizeUrl(url); if (url == null) { LOG.debug("No enclosure URL found for episode " + title); continue; } if (getEpisode(channel.getId(), url) == null) { Long length = null; try { length = new Long(enclosure.getAttributeValue("length")); } catch (Exception x) { LOG.warn("Failed to parse enclosure length.", x); } Date date = parseDate(episodeElement.getChildTextTrim("pubDate")); PodcastEpisode episode = new PodcastEpisode( null, channel.getId(), url, null, title, description, date, duration, length, 0L, PodcastStatus.NEW, null); episodes.add(episode); LOG.info("Created Podcast episode " + title); } } // Sort episode in reverse chronological order (newest first) Collections.sort( episodes, new Comparator<PodcastEpisode>() { public int compare(PodcastEpisode a, PodcastEpisode b) { long timeA = a.getPublishDate() == null ? 0L : a.getPublishDate().getTime(); long timeB = b.getPublishDate() == null ? 0L : b.getPublishDate().getTime(); if (timeA < timeB) { return 1; } if (timeA > timeB) { return -1; } return 0; } }); // Create episodes in database, skipping the proper number of episodes. int downloadCount = settingsService.getPodcastEpisodeDownloadCount(); if (downloadCount == -1) { downloadCount = Integer.MAX_VALUE; } for (int i = 0; i < episodes.size(); i++) { PodcastEpisode episode = episodes.get(i); if (i >= downloadCount) { episode.setStatus(PodcastStatus.SKIPPED); } podcastDao.createEpisode(episode); } }
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { TransferStatus status = null; PlayQueueInputStream in = null; Player player = playerService.getPlayer(request, response, false, true); User user = securityService.getUserByName(player.getUsername()); try { if (!user.isStreamRole()) { response.sendError( HttpServletResponse.SC_FORBIDDEN, "Streaming is forbidden for user " + user.getUsername()); return null; } // If "playlist" request parameter is set, this is a Podcast request. In that case, create a // separate // play queue (in order to support multiple parallel Podcast streams). Integer playlistId = ServletRequestUtils.getIntParameter(request, "playlist"); boolean isPodcast = playlistId != null; if (isPodcast) { PlayQueue playQueue = new PlayQueue(); playQueue.addFiles(false, playlistService.getFilesInPlaylist(playlistId)); player.setPlayQueue(playQueue); Util.setContentLength(response, playQueue.length()); LOG.info("Incoming Podcast request for playlist " + playlistId); } String contentType = StringUtil.getMimeType(request.getParameter("suffix")); response.setContentType(contentType); String preferredTargetFormat = request.getParameter("format"); Integer maxBitRate = ServletRequestUtils.getIntParameter(request, "maxBitRate"); if (Integer.valueOf(0).equals(maxBitRate)) { maxBitRate = null; } VideoTranscodingSettings videoTranscodingSettings = null; // Is this a request for a single file (typically from the embedded Flash player)? // In that case, create a separate playlist (in order to support multiple parallel streams). // Also, enable partial download (HTTP byte range). MediaFile file = getSingleFile(request); boolean isSingleFile = file != null; LongRange range = null; if (isSingleFile) { PlayQueue playQueue = new PlayQueue(); playQueue.addFiles(true, file); player.setPlayQueue(playQueue); if (!file.isVideo()) { response.setIntHeader("ETag", file.getId()); // response.setHeader("Accept-Ranges", "bytes"); } TranscodingService.Parameters parameters = transcodingService.getParameters( file, player, maxBitRate, preferredTargetFormat, null, false); long fileLength = getFileLength(parameters); boolean isConversion = parameters.isDownsample() || parameters.isTranscode(); boolean estimateContentLength = ServletRequestUtils.getBooleanParameter(request, "estimateContentLength", false); boolean isHls = ServletRequestUtils.getBooleanParameter(request, "hls", false); range = getRange(request, file); if (range != null) { LOG.info("Got range: " + range); // response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // Util.setContentLength(response, fileLength - // range.getMinimumLong()); // long firstBytePos = range.getMinimumLong(); // long lastBytePos = fileLength - 1; // response.setHeader("Content-Range", "bytes " + firstBytePos + "-" + // lastBytePos + "/" + fileLength); /// if (isConversion) { response.setHeader("Accept-Ranges", "none"); } else { response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); long maxLength = fileLength; if (maxLength > range.getMaximumLong()) maxLength = range.getMaximumLong() + 1; Util.setContentLength(response, Math.max(maxLength - range.getMinimumLong(), 0)); long firstBytePos = range.getMinimumLong(); long lastBytePos = maxLength - 1; response.setHeader( "Content-Range", "bytes " + firstBytePos + "-" + lastBytePos + "/" + fileLength); } /// } else if (!isHls && (!isConversion || estimateContentLength)) { Util.setContentLength(response, fileLength); } if (isHls) { response.setContentType(StringUtil.getMimeType("ts")); // HLS is always MPEG TS. } else { String transcodedSuffix = transcodingService.getSuffix(player, file, preferredTargetFormat); response.setContentType(StringUtil.getMimeType(transcodedSuffix)); } if (file.isVideo() || isHls) { videoTranscodingSettings = createVideoTranscodingSettings(file, request); } } if (request.getMethod().equals("HEAD")) { return null; } // Terminate any other streams to this player. if (!isPodcast && !isSingleFile) { for (TransferStatus streamStatus : statusService.getStreamStatusesForPlayer(player)) { if (streamStatus.isActive()) { streamStatus.terminate(); } } } status = statusService.createStreamStatus(player); in = new PlayQueueInputStream( player, status, maxBitRate, preferredTargetFormat, videoTranscodingSettings, transcodingService, audioScrobblerService, mediaFileService, searchService); OutputStream out = RangeOutputStream.wrap(response.getOutputStream(), range); // Enabled SHOUTcast, if requested. boolean isShoutCastRequested = "1".equals(request.getHeader("icy-metadata")); if (isShoutCastRequested && !isSingleFile) { response.setHeader("icy-metaint", "" + ShoutCastOutputStream.META_DATA_INTERVAL); response.setHeader("icy-notice1", "This stream is served using FutureSonic"); response.setHeader("icy-notice2", "FutureSonic - Free media streamer - sonic.lt"); response.setHeader("icy-name", "FutureSonic"); response.setHeader("icy-genre", "Mixed"); response.setHeader("icy-url", "http://sonic.lt/"); out = new ShoutCastOutputStream(out, player.getPlayQueue(), settingsService); } final int BUFFER_SIZE = 2048; byte[] buf = new byte[BUFFER_SIZE]; while (true) { // Check if stream has been terminated. if (status.terminated()) { return null; } if (player.getPlayQueue().getStatus() == PlayQueue.Status.STOPPED) { if (isPodcast || isSingleFile) { break; } else { sendDummy(buf, out); } } else { int n = in.read(buf); if (n == -1) { if (isPodcast || isSingleFile) { break; } else { sendDummy(buf, out); } } else { out.write(buf, 0, n); } } } } finally { if (status != null) { securityService.updateUserByteCounts(user, status.getBytesTransfered(), 0L, 0L); statusService.removeStreamStatus(status); } IOUtils.closeQuietly(in); } return null; }
public void execute(JdbcTemplate template) { if (template.queryForInt("select count(*) from version where version = 8") == 0) { LOG.info("Updating database schema to version 8."); template.execute("insert into version values (8)"); } if (!columnExists(template, "show_now_playing", "user_settings")) { LOG.info("Database column 'user_settings.show_now_playing' not found. Creating it."); template.execute( "alter table user_settings add show_now_playing boolean default false not null"); LOG.info("Database column 'user_settings.show_now_playing' was added successfully."); } if (!columnExists(template, "selected_music_folder_id", "user_settings")) { LOG.info("Database column 'user_settings.selected_music_folder_id' not found. Creating it."); template.execute( "alter table user_settings add selected_music_folder_id int default -1 not null"); LOG.info("Database column 'user_settings.selected_music_folder_id' was added successfully."); } if (!tableExists(template, "podcast_channel")) { LOG.info("Database table 'podcast_channel' not found. Creating it."); template.execute( "create table podcast_channel (" + "id identity," + "url varchar not null," + "title varchar," + "description varchar," + "status varchar not null," + "error_message varchar)"); LOG.info("Database table 'podcast_channel' was created successfully."); } if (!tableExists(template, "podcast_episode")) { LOG.info("Database table 'podcast_episode' not found. Creating it."); template.execute( "create table podcast_episode (" + "id identity," + "channel_id int not null," + "url varchar not null," + "path varchar," + "title varchar," + "description varchar," + "publish_date datetime," + "duration varchar," + "bytes_total bigint," + "bytes_downloaded bigint," + "status varchar not null," + "error_message varchar," + "foreign key (channel_id) references podcast_channel(id) on delete cascade)"); LOG.info("Database table 'podcast_episode' was created successfully."); } if (template.queryForInt("select count(*) from role where id = 7") == 0) { LOG.info("Role 'podcast' not found in database. Creating it."); template.execute("insert into role values (7, 'podcast')"); template.execute( "insert into user_role " + "select distinct u.username, 7 from user u, user_role ur " + "where u.username = ur.username and ur.role_id = 1"); LOG.info("Role 'podcast' was created successfully."); } }