private void createPlaylist() { String showName = MediaFileAPI.GetMediaTitle(mediaFile); int numberOfMediaFileSegments = MediaFileAPI.GetNumberOfSegments(mediaFile); Log.debug("Number of media file segments: " + numberOfMediaFileSegments); for (int i = 0; i < numberOfMediaFileSegments; i++) { // get length of current media file segment long mediaFileSegmentDurationInMillis = MediaFileAPI.GetDurationForSegment(mediaFile, i); long mediaFileSegmentDurationInSeconds = mediaFileSegmentDurationInMillis / 1000; // milliseconds to seconds Log.debug( "mediaFileSegmentDurationInMillis (" + i + "): " + mediaFileSegmentDurationInMillis); Log.debug( "mediaFileSegmentDurationInSeconds (" + i + "): " + mediaFileSegmentDurationInSeconds); int sequence = 0; for (int j = 0; j < mediaFileSegmentDurationInSeconds; j += SegmentPlaylist.TARGET_DURATION) { long currentDuration = Math.min(SegmentPlaylist.TARGET_DURATION, mediaFileSegmentDurationInSeconds - j); Segment newSegment = new Segment(currentDuration, sequence, i, showName); segmentList.add(newSegment); sequence++; } } if (segmentList != null) { Log.debug("segmentList size: " + segmentList.size()); } }
public String toString() { StringBuilder sb = new StringBuilder(); String showName = MediaFileAPI.GetMediaTitle(mediaFile); int numberOfMediaFileSegments = MediaFileAPI.GetNumberOfSegments(mediaFile); int segmentCount = (isFileCurrentlyRecording) ? segmentList.size() - 1 : segmentList.size(); Log.debug("Show: " + showName); Log.debug("MediaFileId: " + mediaFileId); Log.debug("Number Of MediaFile segments: " + numberOfMediaFileSegments); Log.debug("Is file currently recording: " + isFileCurrentlyRecording); Log.debug("Number of playlist segments: " + segmentCount); sb.append("#EXTM3U" + LINE_TERM); Log.debug("#EXTM3U"); sb.append("#EXT-X-TARGETDURATION:" + TARGET_DURATION + LINE_TERM); Log.debug("#EXT-X-TARGETDURATION:" + SegmentPlaylist.TARGET_DURATION); // sb.append("#EXT-X-MEDIA-SEQUENCE:1" + LINE_TERM); // Log.debug("#EXT-X-MEDIA-SEQUENCE:1"); for (int i = 0; i < segmentCount; i++) { Segment currentSegment = segmentList.get(i); Segment previousSegment = (i == 0) ? null : segmentList.get(i - 1); Segment nextSegment = (i == segmentCount - 1) ? null : segmentList.get(i + 1); String str = currentSegment.toString(); sb.append(str); // first or last playlist segment for each media file segment // printing the whole playlist makes the log harder to read if ((previousSegment == null) || (nextSegment == null) || (previousSegment.mediaFileSegment != currentSegment.mediaFileSegment) || (currentSegment.mediaFileSegment != nextSegment.mediaFileSegment)) { Log.debug(str); } } // #EXT-X-MEDIA-SEQUENCE:<number> // #EXT-X-PROGRAM-DATE-TIME:<YYYY-MM-DDThh:mm:ssZ> // #EXT-X-ALLOW-CACHE:<YES|NO> // #EXT-X-STREAM-INF if (!isFileCurrentlyRecording) { sb.append("#EXT-X-ENDLIST" + LINE_TERM); Log.debug("#EXT-X-ENDLIST"); } return sb.toString(); }
public boolean canAccept(IMediaResource res) { if (res instanceof IMediaFolder) return true; if (res instanceof IMediaFile) { return (MediaFileAPI.IsBluRay(res.getMediaObject())); } else { return false; } }
public static String GetUserRating(Object MediaObject) { String Rating = MediaFileAPI.GetMediaFileMetadata(MediaObject, "UserRating"); String Ratingscrubbed = ""; if (Rating.contains(".") && Rating.length() != 0) { Ratingscrubbed = Rating.substring(0, Rating.indexOf(".")); } else if (Rating.length() != 0 && !Rating.contains("awaiting")) { Ratingscrubbed = Rating; } else { Ratingscrubbed = "0"; } return Ratingscrubbed; }
public SegmentPlaylist( HttpServletRequest req, Object mediaFile, String conversionId, String quality) { this.req = req; this.mediaFile = mediaFile; this.mediaFileId = MediaFileAPI.GetMediaFileID(mediaFile); this.segmentList = new ArrayList<Segment>(); this.conversionId = conversionId; this.quality = quality; this.isFileCurrentlyRecording = MediaFileAPI.IsFileCurrentlyRecording(mediaFile); if (!this.isFileCurrentlyRecording) { createPlaylist(); } else { // retry until there's at least one full segment recorded while (this.isFileCurrentlyRecording && segmentList.size() <= 1) { this.segmentList.clear(); createPlaylist(); long mediaFileSegmentDurationInMillis = MediaFileAPI.GetDurationForSegment(mediaFile, 0); long mediaFileSegmentDurationInSeconds = mediaFileSegmentDurationInMillis / 1000; // milliseconds to seconds if (this.isFileCurrentlyRecording && (mediaFileSegmentDurationInSeconds < SegmentPlaylist.TARGET_DURATION)) { try { // wait until we'll have a full first segment to return Thread.sleep( (SegmentPlaylist.TARGET_DURATION - mediaFileSegmentDurationInSeconds + 1) * 1000); } catch (InterruptedException e) { Log.warn(e.getMessage()); } } this.isFileCurrentlyRecording = MediaFileAPI.IsFileCurrentlyRecording(mediaFile); } } }
private File getDefaultArtifact(Object mediaObject, MediaArtifactType artifactType) { String def = null; if (artifactType == MediaArtifactType.POSTER) { def = MediaFileAPI.GetMediaFileMetadata( mediaObject, ISageCustomMetadataRW.FieldName.DEFAULT_POSTER); } else if (artifactType == MediaArtifactType.BACKGROUND) { def = MediaFileAPI.GetMediaFileMetadata( mediaObject, ISageCustomMetadataRW.FieldName.DEFAULT_BACKGROUND); } else if (artifactType == MediaArtifactType.BANNER) { def = MediaFileAPI.GetMediaFileMetadata( mediaObject, ISageCustomMetadataRW.FieldName.DEFAULT_BANNER); } if (!StringUtils.isEmpty(def)) { File f = new File(GetFanartCentralFolder(), def); if (f.exists() && f.isFile()) return f; } return null; }
public void SetFanartArtifact( Object mediaObject, File fanart, MediaType mediaType, String mediaTitle, MediaArtifactType artifactType, String artifactTitle, Map<String, String> metadata) { try { String central = (new File(GetFanartCentralFolder())).getCanonicalPath(); String file = fanart.getCanonicalPath(); if (!file.startsWith(central)) { throw new Exception( "You can only set a fanart artifact relative to the fanart folder. Folder: " + central + "; fanart: " + file); } String art = file.substring(central.length()); if (art.startsWith(File.separator)) { art = StringUtils.strip(art, File.separator); } String key = null; if (artifactType == MediaArtifactType.POSTER) { key = ISageCustomMetadataRW.FieldName.DEFAULT_POSTER; } else if (artifactType == MediaArtifactType.BACKGROUND) { key = ISageCustomMetadataRW.FieldName.DEFAULT_BACKGROUND; } else if (artifactType == MediaArtifactType.BANNER) { key = ISageCustomMetadataRW.FieldName.DEFAULT_BANNER; } if (key == null) throw new Exception( "Invalid Artifact Type: " + artifactType + "; Can't set default artifact."); MediaFileAPI.SetMediaFileMetadata(mediaObject, key, art); } catch (Exception e) { log.warn("Failed to set the default fanart artifact!", e); } }
/** * Interface definition for implementation classes that listen for events from the SageTV core * * <p>Variable types are in brackets[] after the var name unless they are the same as the var name * itself. List of known core events: * * <p>MediaFileImported - vars: MediaFile ImportingStarted ImportingCompleted RecordingCompleted * (called when a complete recording is done) vars: MediaFile RecordingStarted (called when any * kind of recording is started) vars: MediaFile RecordingStopped (called whenever a recording is * stopped for any reason) vars: MediaFile AllPluginsLoaded RecordingScheduleChanged * ConflictStatusChanged SystemMessagePosted vars: SystemMessage EPGUpdateCompleted * MediaFileRemoved vars: MediaFile PlaybackStopped (called when the file is closed) vars: * MediaFile, UIContext[String], Duration[Long], MediaTime[Long], ChapterNum[Integer], * TitleNum[Integer] PlaybackFinished (called at the EOF) vars: MediaFile, UIContext[String], * Duration[Long], MediaTime[Long], ChapterNum[Integer], TitleNum[Integer] PlaybackStarted vars: * MediaFile, UIContext[String], Duration[Long], MediaTime[Long], ChapterNum[Integer], * TitleNum[Integer] FavoriteAdded vars: Favorite FavoriteModified vars: Favorite FavoriteRemoved * vars: Favorite PlaylistAdded vars: Playlist, UIContext[String] PlaylistModified vars: Playlist, * UIContext[String] PlaylistRemoved vars: Playlist, UIContext[String] ClientConnected vars: * IPAddress[String], MACAddress[String] (if its a placeshifter/extender, MACAddress is null * otherwise) ClientDisconnected vars: IPAddress[String], MACAddress[String] (if its a * placeshifter/extender, MACAddress is null otherwise) * * <p>This is a callback method invoked from the SageTV core for any events the listener has * subscribed to. See the sage.SageTVPluginRegistry interface definition for details regarding * subscribing and unsubscribing to events. The eventName will be a predefined String which * indicates the event type. The eventVars will be a Map of variables specific to the event * information. This Map should NOT be modified. The keys to the eventVars Map will generally be * Strings; but this may change in the future and plugins that submit events are not required to * follow that rule. */ @Override public synchronized void sageEvent(String eventName, java.util.Map eventVars) { Log.getInstance().write(Log.LOGLEVEL_TRACE, "sageEvent: Event received = " + eventName); // Check that we have the right event. if (!(eventName.startsWith("RecordingCompleted") || eventName.startsWith("RecordingStopped"))) { Log.getInstance() .write(Log.LOGLEVEL_WARN, "sageEvent: Unexpected event received = " + eventName); return; } // Check that we have a valid MediaFile. Object MediaFile = eventVars.get("MediaFile"); if (MediaFile == null) { Log.getInstance().write(Log.LOGLEVEL_WARN, "sageEvent: null MediaFile"); return; } Log.getInstance() .write( Log.LOGLEVEL_TRACE, "sageEvent: Finished recording " + AiringAPI.GetAiringTitle(MediaFile) + " - " + ShowAPI.GetShowEpisode(MediaFile)); // If it's a Manual, Favorite, or TimedRecord (manual) we do not need to worry about it. if (AiringAPI.IsFavorite(MediaFile) || AiringAPI.IsManualRecord(MediaFile)) { Log.getInstance().write(Log.LOGLEVEL_TRACE, "sageEvent: Is not an Intelligent Recording."); return; } // Create the DataStore which will allow us to access the data for this MediaFile. DataStore store = new DataStore(MediaFile); int maxToKeep; // If it's monitored keep the number specified. If it's not monitored use the // global default. if (store.isMonitored()) { Log.getInstance().write(Log.LOGLEVEL_TRACE, "sageEvent: Using max for this show."); maxToKeep = store.getMax(); } else { Log.getInstance().write(Log.LOGLEVEL_TRACE, "sageEvent: Using global max."); maxToKeep = Util.GetIntProperty(PROPERTY_DEFAULT_MAX, DEFAULT_MAX_STRING); } Log.getInstance() .write( Log.LOGLEVEL_TRACE, "sageEvent: Max to keep = " + (maxToKeep == DEFAULT_MAX ? "unlimited" : maxToKeep)); // See how many are already recorded. int numberRecorded = Util.getNumberRecorded(MediaFile); Log.getInstance() .write(Log.LOGLEVEL_TRACE, "sageEvent: Number already recorded = " + numberRecorded); // If it's unlimited or below the threshhold don't worry about it. if (maxToKeep == UNLIMITED || numberRecorded <= maxToKeep) { Log.getInstance().write(Log.LOGLEVEL_TRACE, "sageEvent: Below threshhold."); return; } Log.getInstance() .write( Log.LOGLEVEL_TRACE, "sageEvent: Threshhold exceeded. Deleting one or more " + AiringAPI.GetAiringTitle(MediaFile)); // Get the direction to sort. boolean keepOldest = Configuration.GetServerProperty(PROPERTY_KEEP_OLDEST, "true").equalsIgnoreCase("true"); Log.getInstance().write(Log.LOGLEVEL_TRACE, "sageEvent: Keep oldest = " + keepOldest); // Get all of the recordings in the proper order. Recordings at the beginning of the // List will be deleted first. List<Object> allRecorded = Util.getAllRecorded(MediaFile, "GetAiringStartTime", keepOldest); Log.getInstance() .write(Log.LOGLEVEL_TRACE, "sageEvent: Sorted list size = " + allRecorded.size()); if (Log.getInstance().GetLogLevel() <= Log.LOGLEVEL_VERBOSE) { for (Object MF : allRecorded) Log.getInstance() .write( Log.LOGLEVEL_VERBOSE, "sageEvent: Date recorded = " + Utility.PrintDateLong(AiringAPI.GetAiringStartTime(MF)) + " : " + Utility.PrintTimeLong(AiringAPI.GetAiringStartTime(MF)) + " - " + AiringAPI.GetAiringTitle(MF) + " - " + ShowAPI.GetShowEpisode(MF)); } boolean reduceToMax = Configuration.GetServerProperty(PROPERTY_REDUCE_TO_MAX, "false").equalsIgnoreCase("true"); // Calculate how many to delete. int numberToDelete = (reduceToMax ? numberRecorded - maxToKeep : 1); Log.getInstance().write(Log.LOGLEVEL_TRACE, "sageEvent: Need to delete " + numberToDelete); // Sanity check. if (allRecorded == null || allRecorded.size() < numberToDelete || numberToDelete < 1) { Log.getInstance() .write( Log.LOGLEVEL_WARN, "sageEvent: Internal error. numberToDelete exceeds allRecorded. Deleting this MediaFile."); MediaFileAPI.DeleteFile(MediaFile); return; } for (int i = 0; i < numberToDelete; i++) { Object MF = allRecorded.get(i); // Log.getInstance().write(Log.LOGLEVEL_TRACE, "sageEvent: TESTMODE. Would have deleted " + // AiringAPI.GetAiringTitle(MF) + " - " + ShowAPI.GetShowEpisode(MF)); if (MediaFileAPI.DeleteFile(MF)) Log.getInstance() .write( Log.LOGLEVEL_TRACE, "sageEvent: Deleted " + AiringAPI.GetAiringTitle(MF) + " - " + ShowAPI.GetShowEpisode(MF)); else Log.getInstance() .write( Log.LOGLEVEL_WARN, "sageEvent: Failed to delete " + AiringAPI.GetAiringTitle(MF) + " - " + ShowAPI.GetShowEpisode(MF)); } }