Example #1
0
  /**
   * 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));
    }
  }