{"action":"created","comment":{"url":"https://api.github.com/repos/opencast/opencast/pulls/comments/192343846","pull_request_review_id":125096783,"id":192343846,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5MjM0Mzg0Ng==","diff_hunk":"@@ -392,4 +462,530 @@ private void handleEncoderOutput(List<File> output, String message) {\n     }\n   }\n \n+  /**\n+   * Rewrite multiple profiles to ffmpeg complex filter filtergraph chains - inputs are passed in as options, eq: [0aa]\n+   * and [0vv] Any filters in the encoding profiles are moved into a clause in the complex filter chain for each output\n+   */\n+  protected class OutputAggregate {\n+    private final List<EncodingProfile> pf;\n+    private final ArrayList<String> outputs = new ArrayList<String>();\n+    private final ArrayList<String> outputFiles = new ArrayList<String>();\n+    private final ArrayList<String> vpads; // output pads for each segment\n+    private final ArrayList<String> apads;\n+    private final ArrayList<String> vfilter; // filters for each output format\n+    private final ArrayList<String> afilter;\n+    private String vInputPad = \"\";\n+    private String aInputPad = \"\";\n+    private String vsplit = \"\";\n+    private String asplit = \"\";\n+\n+    /*\n+     * set the audio filter if there are any in the profiles or identity\n+     */\n+    private void setAudioFilters() {\n+      if (pf.size() == 1) {\n+        if (afilter.get(0) != null)\n+          afilter.set(0, aInputPad + afilter.get(0) + apads.get(0)); // Use audio filter on input directly\n+      } else\n+        for (int i = 0; i < pf.size(); i++) {\n+          if (afilter.get(i) != null) {\n+            afilter.set(i, \"[oa0\" + i + \"]\" + afilter.get(i) + apads.get(i)); // Use audio filter on apad\n+            asplit += \"[oa0\" + i + \"]\";\n+          } else {\n+            asplit += apads.get(i); // straight to output\n+          }\n+        }\n+      afilter.removeAll(Arrays.asList((String) null));\n+    }\n+\n+    /*\n+     * set the video filter if there are any in the profiles\n+     */\n+    private void setVideoFilters() {\n+      if (pf.size() == 1) {\n+        if (vfilter.get(0) != null)\n+          vfilter.set(0, vInputPad + vfilter.get(0) + vpads.get(0)); // send to filter first\n+      } else\n+        for (int i = 0; i < pf.size(); i++) {\n+          if (vfilter.get(i) != null) {\n+            vfilter.set(i, \"[ov0\" + i + \"]\" + vfilter.get(i) + vpads.get(i)); // send to filter first\n+            vsplit += \"[ov0\" + i + \"]\";\n+          } else {\n+            vsplit += vpads.get(i);// straight to output\n+          }\n+        }\n+\n+      vfilter.removeAll(Arrays.asList((String) null));\n+    }\n+\n+    public List<String> getOutFiles() {\n+      return outputFiles;\n+    }\n+\n+    public List<String> getOutput() {\n+      return outputs;\n+    }\n+\n+    /**\n+     *\n+     * @return filter split clause for ffmpeg\n+     */\n+    public String getVsplit() {\n+      return vsplit;\n+    }\n+\n+    public String getAsplit() {\n+      return asplit;\n+    }\n+\n+    public String getVideoFilter() {\n+      if (vfilter.isEmpty())\n+        return null;\n+      return StringUtils.join(vfilter, \";\");\n+    }\n+\n+    public String getAudioFilter() {\n+      if (afilter.isEmpty())\n+        return null;\n+      return StringUtils.join(afilter, \";\");\n+    }\n+\n+    /**\n+     * If this is a raw mapping not used with complex filter, strip the square brackets if there are any\n+     *\n+     * @param pad\n+     *          - such as 0:a, [0:v], [1:1],[0:12],[main],[overlay]\n+     * @return adjusted pad\n+     */\n+    public String adjustForNoComplexFilter(String pad) {\n+      final Pattern outpad = Pattern.compile(\"\\\\[(\\\\d+:[av\\\\d{1,2}])\\\\]\");\n+      try {\n+        Matcher matcher = outpad.matcher(pad); // throws exception if pad is null\n+        if (matcher != null && matcher.matches()) {\n+          logger.info(pad + matcher.toString());\n+          return matcher.group(1);\n+        }\n+      } catch (Exception e) {\n+      }\n+      return pad;\n+    }\n+\n+    /**\n+     * Replace all the templates with real values for each profile\n+     *\n+     * @param cmd\n+     *          from profile\n+     * @param params\n+     *          from input\n+     * @return command\n+     */\n+    protected String processParameters(String cmd, Map<String, String> params) {\n+      String r = cmd;\n+      for (Map.Entry<String, String> e : params.entrySet()) {\n+        r = r.replace(\"#{\" + e.getKey() + \"}\", e.getValue());\n+      }\n+      return r;\n+    }\n+\n+    /**\n+     * Translate the profiles to work with complex filter clauses in ffmpeg, it splits one output into multiple, one for\n+     * each encoding profile\n+     *\n+     * @param profiles\n+     *          - list of encoding profiles\n+     * @param params\n+     *          - values for substitution\n+     * @param vInputPad\n+     *          - name of video pad as input, eg: [0v] null if no video\n+     * @param aInputPad\n+     *          - name of audio pad as input, eg [0a], null if no audio\n+     * @throws EncoderException\n+     *           - if it fails\n+     */\n+    public OutputAggregate(List<EncodingProfile> profiles, Map<String, String> params, String vInputPad,\n+            String aInputPad) throws EncoderException {\n+      pf = profiles;\n+      if (vInputPad == null && aInputPad == null)\n+        throw new EncoderException(\"At least one of video or audio input must be specified\");\n+      IdBuilder idbuilder = IdBuilderFactory.newInstance().newIdBuilder();\n+      int size = profiles.size();\n+      // Init\n+      vfilter = new ArrayList<String>(java.util.Collections.nCopies(size, null));\n+      afilter = new ArrayList<String>(java.util.Collections.nCopies(size, null));\n+      // name of output pads to map to files\n+      apads = new ArrayList<String>(java.util.Collections.nCopies(size, null));\n+      vpads = new ArrayList<String>(java.util.Collections.nCopies(size, null));\n+\n+      vsplit = (size > 1) ? (vInputPad + \"split=\" + size) : null; // number of splits\n+      asplit = (size > 1) ? (aInputPad + \"asplit=\" + size) : null;\n+      this.vInputPad = vInputPad;\n+      this.aInputPad = aInputPad;\n+\n+      int indx = 0; // profiles\n+      for (EncodingProfile profile : profiles) {\n+        String cmd = \"\";\n+        String outSuffix;\n+        // generate random name as we only have one base name\n+        String outFileName = params.get(\"out.name.base\") + \"_\" + idbuilder.createNew().toString();\n+        params.put(\"out.name\", outFileName); // Output file name for this profile\n+        try {\n+          outSuffix = processParameters(profile.getSuffix(), params);\n+          params.put(\"out.suffix\", outSuffix); // Add profile suffix\n+        } catch (Exception e) {\n+          throw new EncoderException(\"Missing Encoding Profiles\");\n+        }\n+        // substitute the output file name\n+        String r = profile.getExtension(CMD_SUFFIX); // Get ffmpeg command\n+        if (r == null)\n+          throw new EncoderException(\"Missing Encoding Profile \" + profile.getIdentifier() + \" ffmpeg command\");\n+        for (Map.Entry<String, String> e : params.entrySet()) { // replace output filenames\n+          r = r.replace(\"#{\" + e.getKey() + \"}\", e.getValue());\n+        }\n+        r = r.replace(\"#{space}\", \" \");\n+        String[] arguments;\n+        try {\n+          arguments = CommandLineUtils.translateCommandline(r);\n+        } catch (Exception e) {\n+          throw new EncoderException(\"Could not parse encoding profile command line\", e);\n+        }\n+        List<String> sl = Arrays.asList(arguments);\n+        // Find and remove input and filters\n+        int i = 0;\n+        while (i < sl.size()) {\n+          String opt = sl.get(i);\n+          if (opt.startsWith(\"-vf\") || opt.startsWith(\"-filter:v\")) { // video filters\n+            vfilter.set(indx, sl.get(i + 1).replace(\"\\\"\", \"\")); // store without quotes\n+            i++;\n+          } else if (opt.startsWith(\"-filter_complex\") || opt.startsWith(\"-lavfi\")) { // safer to quit now than to\n+            // baffle users with strange errors later\n+            i++;\n+            logger.error(\"Command does not support complex filters - only simple -af or -vf filters are supported\");\n+            throw new EncoderException(\n+                    \"Cannot parse complex filters in\" + profile.getIdentifier() + \" for this operation\");\n+          } else if (opt.startsWith(\"-af\") || opt.startsWith(\"-filter:a\")) { // audio filter\n+            afilter.set(indx, sl.get(i + 1).replace(\"\\\"\", \"\")); // store without quotes\n+            i++;\n+          } else if (\"-i\".equals(opt)) {\n+            i++; // inputs are now mapped, remove from command\n+          } else if (opt.startsWith(\"-c:\") || opt.startsWith(\"-codec:\") || opt.contains(\"-vcodec\")\n+                  || opt.contains(\"-acodec\")) { // cannot copy codec im complex filter\n+            String str = sl.get(i + 1);\n+            if (str.contains(\"copy\")) // c\n+              i++;\n+            else\n+              cmd = cmd + \" \" + opt;\n+          } else { // keep the rest\n+            cmd = cmd + \" \" + opt;\n+          }\n+          i++;\n+        }\n+        /* Remove unused commandline parts */\n+        cmd = cmd.replaceAll(\"#\\\\{.*?\\\\}\", \"\");\n+        // Find the output map based on splits and filters\n+        if (size == 1) { // no split\n+          if (afilter.get(indx) == null)\n+            apads.set(indx, adjustForNoComplexFilter(aInputPad));\n+          else\n+            apads.set(indx, \"[oa\" + indx + \"]\");\n+          if (vfilter.get(indx) == null)\n+            vpads.set(indx, adjustForNoComplexFilter(vInputPad)); // No split, no filter - straight from input\n+          else\n+            vpads.set(indx, \"[ov\" + indx + \"]\");\n+\n+        } else { // split\n+          vpads.set(indx, \"[ov\" + indx + \"]\"); // name the output pads from split -> input to final format\n+          apads.set(indx, \"[oa\" + indx + \"]\"); // name the output audio pads\n+        }\n+        cmd = StringUtils.trimToNull(cmd); // remove all leading/trailing whitespaces\n+        if (cmd != null) { // What is more unsafe?\n+          outputFiles.add(sl.get(sl.size() - 1)); // unsafe, (like above in getOutputFilen)\n+          // alternative is also unsafe - #{out.dir}/#{out.name}#{out.suffix}\n+          // outputFiles.add(outDir + outFileName + outSuffix);\n+          if (vInputPad != null) {\n+            outputs.add(\"-map \" + vpads.get(indx));\n+          }\n+          if (aInputPad != null) {\n+            outputs.add(\"-map \" + apads.get(indx)); // map video and audio input\n+          }\n+          outputs.add(cmd); // profiles appended in order, they are numbered 0,1,2,3...\n+          indx++; // indx for this profile\n+        }\n+      }\n+      setVideoFilters();\n+      setAudioFilters();\n+    }\n+  }\n+\n+  /**\n+   * Clean up the edit points, make sure the gap between consecutive segments are larger than the transition Otherwise\n+   * it can be very slow to run and output will be ugly because the fades will extend the clip\n+   *\n+   * @param edits\n+   *          - clips to be stitched together\n+   * @param gap\n+   *          = transitionDuration / 1000; default gap size - same as fade\n+   * @return\n+   */\n+  private static List<VideoClip> sortSegments(List<VideoClip> edits, double gap) {\n+    // TODO: add transition to each edited clip in place of transitionDuration","path":"modules/composer-ffmpeg/src/main/java/org/opencastproject/composer/impl/EncoderEngine.java","position":362,"original_position":362,"commit_id":"84a4711449918a668724843a3f866204ee47fd28","original_commit_id":"84a4711449918a668724843a3f866204ee47fd28","user":{"login":"staubesv","id":1590263,"node_id":"MDQ6VXNlcjE1OTAyNjM=","avatar_url":"https://avatars0.githubusercontent.com/u/1590263?v=4","gravatar_id":"","url":"https://api.github.com/users/staubesv","html_url":"https://github.com/staubesv","followers_url":"https://api.github.com/users/staubesv/followers","following_url":"https://api.github.com/users/staubesv/following{/other_user}","gists_url":"https://api.github.com/users/staubesv/gists{/gist_id}","starred_url":"https://api.github.com/users/staubesv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/staubesv/subscriptions","organizations_url":"https://api.github.com/users/staubesv/orgs","repos_url":"https://api.github.com/users/staubesv/repos","events_url":"https://api.github.com/users/staubesv/events{/privacy}","received_events_url":"https://api.github.com/users/staubesv/received_events","type":"User","site_admin":false},"body":"JIRA instead of comment","created_at":"2018-06-01T09:29:47Z","updated_at":"2018-06-01T09:39:28Z","html_url":"https://github.com/opencast/opencast/pull/264#discussion_r192343846","pull_request_url":"https://api.github.com/repos/opencast/opencast/pulls/264","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/opencast/opencast/pulls/comments/192343846"},"html":{"href":"https://github.com/opencast/opencast/pull/264#discussion_r192343846"},"pull_request":{"href":"https://api.github.com/repos/opencast/opencast/pulls/264"}}},"pull_request":{"url":"https://api.github.com/repos/opencast/opencast/pulls/264","id":190705664,"node_id":"MDExOlB1bGxSZXF1ZXN0MTkwNzA1NjY0","html_url":"https://github.com/opencast/opencast/pull/264","diff_url":"https://github.com/opencast/opencast/pull/264.diff","patch_url":"https://github.com/opencast/opencast/pull/264.patch","issue_url":"https://api.github.com/repos/opencast/opencast/issues/264","number":264,"state":"open","locked":false,"title":"Process-Smil - edit and encode to multiple delivery formats","user":{"login":"phoebem","id":6550068,"node_id":"MDQ6VXNlcjY1NTAwNjg=","avatar_url":"https://avatars3.githubusercontent.com/u/6550068?v=4","gravatar_id":"","url":"https://api.github.com/users/phoebem","html_url":"https://github.com/phoebem","followers_url":"https://api.github.com/users/phoebem/followers","following_url":"https://api.github.com/users/phoebem/following{/other_user}","gists_url":"https://api.github.com/users/phoebem/gists{/gist_id}","starred_url":"https://api.github.com/users/phoebem/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phoebem/subscriptions","organizations_url":"https://api.github.com/users/phoebem/orgs","repos_url":"https://api.github.com/users/phoebem/repos","events_url":"https://api.github.com/users/phoebem/events{/privacy}","received_events_url":"https://api.github.com/users/phoebem/received_events","type":"User","site_admin":false},"body":"This is the process-smil part of 12607, updated to new composer code.","created_at":"2018-05-25T22:24:01Z","updated_at":"2018-06-01T09:39:28Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d3bc603d609ec5c3c682c546f3713d7e0bb3977a","assignee":{"login":"staubesv","id":1590263,"node_id":"MDQ6VXNlcjE1OTAyNjM=","avatar_url":"https://avatars0.githubusercontent.com/u/1590263?v=4","gravatar_id":"","url":"https://api.github.com/users/staubesv","html_url":"https://github.com/staubesv","followers_url":"https://api.github.com/users/staubesv/followers","following_url":"https://api.github.com/users/staubesv/following{/other_user}","gists_url":"https://api.github.com/users/staubesv/gists{/gist_id}","starred_url":"https://api.github.com/users/staubesv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/staubesv/subscriptions","organizations_url":"https://api.github.com/users/staubesv/orgs","repos_url":"https://api.github.com/users/staubesv/repos","events_url":"https://api.github.com/users/staubesv/events{/privacy}","received_events_url":"https://api.github.com/users/staubesv/received_events","type":"User","site_admin":false},"assignees":[{"login":"staubesv","id":1590263,"node_id":"MDQ6VXNlcjE1OTAyNjM=","avatar_url":"https://avatars0.githubusercontent.com/u/1590263?v=4","gravatar_id":"","url":"https://api.github.com/users/staubesv","html_url":"https://github.com/staubesv","followers_url":"https://api.github.com/users/staubesv/followers","following_url":"https://api.github.com/users/staubesv/following{/other_user}","gists_url":"https://api.github.com/users/staubesv/gists{/gist_id}","starred_url":"https://api.github.com/users/staubesv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/staubesv/subscriptions","organizations_url":"https://api.github.com/users/staubesv/orgs","repos_url":"https://api.github.com/users/staubesv/repos","events_url":"https://api.github.com/users/staubesv/events{/privacy}","received_events_url":"https://api.github.com/users/staubesv/received_events","type":"User","site_admin":false}],"requested_reviewers":[{"login":"lkiesow","id":1008395,"node_id":"MDQ6VXNlcjEwMDgzOTU=","avatar_url":"https://avatars3.githubusercontent.com/u/1008395?v=4","gravatar_id":"","url":"https://api.github.com/users/lkiesow","html_url":"https://github.com/lkiesow","followers_url":"https://api.github.com/users/lkiesow/followers","following_url":"https://api.github.com/users/lkiesow/following{/other_user}","gists_url":"https://api.github.com/users/lkiesow/gists{/gist_id}","starred_url":"https://api.github.com/users/lkiesow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lkiesow/subscriptions","organizations_url":"https://api.github.com/users/lkiesow/orgs","repos_url":"https://api.github.com/users/lkiesow/repos","events_url":"https://api.github.com/users/lkiesow/events{/privacy}","received_events_url":"https://api.github.com/users/lkiesow/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/opencast/opencast/pulls/264/commits","review_comments_url":"https://api.github.com/repos/opencast/opencast/pulls/264/comments","review_comment_url":"https://api.github.com/repos/opencast/opencast/pulls/comments{/number}","comments_url":"https://api.github.com/repos/opencast/opencast/issues/264/comments","statuses_url":"https://api.github.com/repos/opencast/opencast/statuses/84a4711449918a668724843a3f866204ee47fd28","head":{"label":"phoebem:f/MH-12607-part-2-process-smil","ref":"f/MH-12607-part-2-process-smil","sha":"84a4711449918a668724843a3f866204ee47fd28","user":{"login":"phoebem","id":6550068,"node_id":"MDQ6VXNlcjY1NTAwNjg=","avatar_url":"https://avatars3.githubusercontent.com/u/6550068?v=4","gravatar_id":"","url":"https://api.github.com/users/phoebem","html_url":"https://github.com/phoebem","followers_url":"https://api.github.com/users/phoebem/followers","following_url":"https://api.github.com/users/phoebem/following{/other_user}","gists_url":"https://api.github.com/users/phoebem/gists{/gist_id}","starred_url":"https://api.github.com/users/phoebem/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phoebem/subscriptions","organizations_url":"https://api.github.com/users/phoebem/orgs","repos_url":"https://api.github.com/users/phoebem/repos","events_url":"https://api.github.com/users/phoebem/events{/privacy}","received_events_url":"https://api.github.com/users/phoebem/received_events","type":"User","site_admin":false},"repo":{"id":134437629,"node_id":"MDEwOlJlcG9zaXRvcnkxMzQ0Mzc2Mjk=","name":"opencast","full_name":"phoebem/opencast","owner":{"login":"phoebem","id":6550068,"node_id":"MDQ6VXNlcjY1NTAwNjg=","avatar_url":"https://avatars3.githubusercontent.com/u/6550068?v=4","gravatar_id":"","url":"https://api.github.com/users/phoebem","html_url":"https://github.com/phoebem","followers_url":"https://api.github.com/users/phoebem/followers","following_url":"https://api.github.com/users/phoebem/following{/other_user}","gists_url":"https://api.github.com/users/phoebem/gists{/gist_id}","starred_url":"https://api.github.com/users/phoebem/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phoebem/subscriptions","organizations_url":"https://api.github.com/users/phoebem/orgs","repos_url":"https://api.github.com/users/phoebem/repos","events_url":"https://api.github.com/users/phoebem/events{/privacy}","received_events_url":"https://api.github.com/users/phoebem/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/phoebem/opencast","description":"The free and open source solution for automated video capture and distribution at scale.","fork":true,"url":"https://api.github.com/repos/phoebem/opencast","forks_url":"https://api.github.com/repos/phoebem/opencast/forks","keys_url":"https://api.github.com/repos/phoebem/opencast/keys{/key_id}","collaborators_url":"https://api.github.com/repos/phoebem/opencast/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/phoebem/opencast/teams","hooks_url":"https://api.github.com/repos/phoebem/opencast/hooks","issue_events_url":"https://api.github.com/repos/phoebem/opencast/issues/events{/number}","events_url":"https://api.github.com/repos/phoebem/opencast/events","assignees_url":"https://api.github.com/repos/phoebem/opencast/assignees{/user}","branches_url":"https://api.github.com/repos/phoebem/opencast/branches{/branch}","tags_url":"https://api.github.com/repos/phoebem/opencast/tags","blobs_url":"https://api.github.com/repos/phoebem/opencast/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/phoebem/opencast/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/phoebem/opencast/git/refs{/sha}","trees_url":"https://api.github.com/repos/phoebem/opencast/git/trees{/sha}","statuses_url":"https://api.github.com/repos/phoebem/opencast/statuses/{sha}","languages_url":"https://api.github.com/repos/phoebem/opencast/languages","stargazers_url":"https://api.github.com/repos/phoebem/opencast/stargazers","contributors_url":"https://api.github.com/repos/phoebem/opencast/contributors","subscribers_url":"https://api.github.com/repos/phoebem/opencast/subscribers","subscription_url":"https://api.github.com/repos/phoebem/opencast/subscription","commits_url":"https://api.github.com/repos/phoebem/opencast/commits{/sha}","git_commits_url":"https://api.github.com/repos/phoebem/opencast/git/commits{/sha}","comments_url":"https://api.github.com/repos/phoebem/opencast/comments{/number}","issue_comment_url":"https://api.github.com/repos/phoebem/opencast/issues/comments{/number}","contents_url":"https://api.github.com/repos/phoebem/opencast/contents/{+path}","compare_url":"https://api.github.com/repos/phoebem/opencast/compare/{base}...{head}","merges_url":"https://api.github.com/repos/phoebem/opencast/merges","archive_url":"https://api.github.com/repos/phoebem/opencast/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/phoebem/opencast/downloads","issues_url":"https://api.github.com/repos/phoebem/opencast/issues{/number}","pulls_url":"https://api.github.com/repos/phoebem/opencast/pulls{/number}","milestones_url":"https://api.github.com/repos/phoebem/opencast/milestones{/number}","notifications_url":"https://api.github.com/repos/phoebem/opencast/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/phoebem/opencast/labels{/name}","releases_url":"https://api.github.com/repos/phoebem/opencast/releases{/id}","deployments_url":"https://api.github.com/repos/phoebem/opencast/deployments","created_at":"2018-05-22T15:41:15Z","updated_at":"2018-05-25T23:01:21Z","pushed_at":"2018-05-30T17:40:38Z","git_url":"git://github.com/phoebem/opencast.git","ssh_url":"git@github.com:phoebem/opencast.git","clone_url":"https://github.com/phoebem/opencast.git","svn_url":"https://github.com/phoebem/opencast","homepage":"http://opencast.org","size":136328,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"ecl-2.0","name":"Educational Community License v2.0","spdx_id":"ECL-2.0","url":"https://api.github.com/licenses/ecl-2.0","node_id":"MDc6TGljZW5zZTMw"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"opencast:develop","ref":"develop","sha":"1857deaddcc743825a3717006e694c7be8df8565","user":{"login":"opencast","id":5639632,"node_id":"MDEyOk9yZ2FuaXphdGlvbjU2Mzk2MzI=","avatar_url":"https://avatars0.githubusercontent.com/u/5639632?v=4","gravatar_id":"","url":"https://api.github.com/users/opencast","html_url":"https://github.com/opencast","followers_url":"https://api.github.com/users/opencast/followers","following_url":"https://api.github.com/users/opencast/following{/other_user}","gists_url":"https://api.github.com/users/opencast/gists{/gist_id}","starred_url":"https://api.github.com/users/opencast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opencast/subscriptions","organizations_url":"https://api.github.com/users/opencast/orgs","repos_url":"https://api.github.com/users/opencast/repos","events_url":"https://api.github.com/users/opencast/events{/privacy}","received_events_url":"https://api.github.com/users/opencast/received_events","type":"Organization","site_admin":false},"repo":{"id":115198687,"node_id":"MDEwOlJlcG9zaXRvcnkxMTUxOTg2ODc=","name":"opencast","full_name":"opencast/opencast","owner":{"login":"opencast","id":5639632,"node_id":"MDEyOk9yZ2FuaXphdGlvbjU2Mzk2MzI=","avatar_url":"https://avatars0.githubusercontent.com/u/5639632?v=4","gravatar_id":"","url":"https://api.github.com/users/opencast","html_url":"https://github.com/opencast","followers_url":"https://api.github.com/users/opencast/followers","following_url":"https://api.github.com/users/opencast/following{/other_user}","gists_url":"https://api.github.com/users/opencast/gists{/gist_id}","starred_url":"https://api.github.com/users/opencast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opencast/subscriptions","organizations_url":"https://api.github.com/users/opencast/orgs","repos_url":"https://api.github.com/users/opencast/repos","events_url":"https://api.github.com/users/opencast/events{/privacy}","received_events_url":"https://api.github.com/users/opencast/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/opencast/opencast","description":"The free and open source solution for automated video capture and distribution at scale.","fork":false,"url":"https://api.github.com/repos/opencast/opencast","forks_url":"https://api.github.com/repos/opencast/opencast/forks","keys_url":"https://api.github.com/repos/opencast/opencast/keys{/key_id}","collaborators_url":"https://api.github.com/repos/opencast/opencast/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/opencast/opencast/teams","hooks_url":"https://api.github.com/repos/opencast/opencast/hooks","issue_events_url":"https://api.github.com/repos/opencast/opencast/issues/events{/number}","events_url":"https://api.github.com/repos/opencast/opencast/events","assignees_url":"https://api.github.com/repos/opencast/opencast/assignees{/user}","branches_url":"https://api.github.com/repos/opencast/opencast/branches{/branch}","tags_url":"https://api.github.com/repos/opencast/opencast/tags","blobs_url":"https://api.github.com/repos/opencast/opencast/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/opencast/opencast/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/opencast/opencast/git/refs{/sha}","trees_url":"https://api.github.com/repos/opencast/opencast/git/trees{/sha}","statuses_url":"https://api.github.com/repos/opencast/opencast/statuses/{sha}","languages_url":"https://api.github.com/repos/opencast/opencast/languages","stargazers_url":"https://api.github.com/repos/opencast/opencast/stargazers","contributors_url":"https://api.github.com/repos/opencast/opencast/contributors","subscribers_url":"https://api.github.com/repos/opencast/opencast/subscribers","subscription_url":"https://api.github.com/repos/opencast/opencast/subscription","commits_url":"https://api.github.com/repos/opencast/opencast/commits{/sha}","git_commits_url":"https://api.github.com/repos/opencast/opencast/git/commits{/sha}","comments_url":"https://api.github.com/repos/opencast/opencast/comments{/number}","issue_comment_url":"https://api.github.com/repos/opencast/opencast/issues/comments{/number}","contents_url":"https://api.github.com/repos/opencast/opencast/contents/{+path}","compare_url":"https://api.github.com/repos/opencast/opencast/compare/{base}...{head}","merges_url":"https://api.github.com/repos/opencast/opencast/merges","archive_url":"https://api.github.com/repos/opencast/opencast/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/opencast/opencast/downloads","issues_url":"https://api.github.com/repos/opencast/opencast/issues{/number}","pulls_url":"https://api.github.com/repos/opencast/opencast/pulls{/number}","milestones_url":"https://api.github.com/repos/opencast/opencast/milestones{/number}","notifications_url":"https://api.github.com/repos/opencast/opencast/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/opencast/opencast/labels{/name}","releases_url":"https://api.github.com/repos/opencast/opencast/releases{/id}","deployments_url":"https://api.github.com/repos/opencast/opencast/deployments","created_at":"2017-12-23T13:54:35Z","updated_at":"2018-05-29T21:18:52Z","pushed_at":"2018-06-01T07:28:40Z","git_url":"git://github.com/opencast/opencast.git","ssh_url":"git@github.com:opencast/opencast.git","clone_url":"https://github.com/opencast/opencast.git","svn_url":"https://github.com/opencast/opencast","homepage":"http://opencast.org","size":136309,"stargazers_count":25,"watchers_count":25,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":38,"mirror_url":null,"archived":false,"open_issues_count":27,"license":{"key":"ecl-2.0","name":"Educational Community License v2.0","spdx_id":"ECL-2.0","url":"https://api.github.com/licenses/ecl-2.0","node_id":"MDc6TGljZW5zZTMw"},"forks":38,"open_issues":27,"watchers":25,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/opencast/opencast/pulls/264"},"html":{"href":"https://github.com/opencast/opencast/pull/264"},"issue":{"href":"https://api.github.com/repos/opencast/opencast/issues/264"},"comments":{"href":"https://api.github.com/repos/opencast/opencast/issues/264/comments"},"review_comments":{"href":"https://api.github.com/repos/opencast/opencast/pulls/264/comments"},"review_comment":{"href":"https://api.github.com/repos/opencast/opencast/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/opencast/opencast/pulls/264/commits"},"statuses":{"href":"https://api.github.com/repos/opencast/opencast/statuses/84a4711449918a668724843a3f866204ee47fd28"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"staubesv"}}