View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.media.video;
20  
21  import java.io.IOException;
22  import java.net.URISyntaxException;
23  import java.nio.file.Files;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Optional;
28  import java.util.function.Supplier;
29  
30  import org.apache.logging.log4j.LogManager;
31  import org.mycore.common.MCRSessionMgr;
32  import org.mycore.common.config.MCRConfiguration2;
33  import org.mycore.datamodel.metadata.MCRObjectID;
34  import org.mycore.datamodel.niofs.MCRAbstractFileStore;
35  import org.mycore.datamodel.niofs.MCRPath;
36  import org.mycore.frontend.filter.MCRSecureTokenV2FilterConfig;
37  import org.mycore.frontend.support.MCRSecureTokenV2;
38  import org.mycore.media.MCRMediaSourceType;
39  
40  public class MCRMediaSourceProvider {
41      private static Optional<String> wowzaBaseURL = MCRConfiguration2.getString("MCR.Media.Wowza.BaseURL");
42  
43      private static Optional<String> wowzaRTMPBaseURL = MCRConfiguration2
44          .getString("MCR.Media.Wowza.RTMPBaseURL");
45  
46      private static String wowzaHashParameter = MCRConfiguration2.getString("MCR.Media.Wowza.HashParameter")
47          .orElse("wowzatokenhash");
48  
49      static {
50          MCRConfiguration2.addPropertyChangeEventLister(p -> p.startsWith("MCR.Media.Wowza"),
51              MCRMediaSourceProvider::updateWowzaSettings);
52      }
53  
54      private Optional<MCRSecureTokenV2> wowzaToken;
55  
56      private ArrayList<MCRMediaSource> sources;
57  
58      public MCRMediaSourceProvider(String derivateId, String path, Optional<String> userAgent,
59          Supplier<String[]> parameterSupplier) throws IOException, URISyntaxException {
60          try {
61              wowzaToken = wowzaBaseURL.map(
62                  (w) -> new MCRSecureTokenV2(
63                      MCRConfiguration2.getStringOrThrow("MCR.Media.Wowza.ContentPathPrefix")
64                          + getContentPath(derivateId, path),
65                      MCRSessionMgr.getCurrentSession().getCurrentIP(),
66                      MCRConfiguration2.getStringOrThrow("MCR.Media.Wowza.SharedSecred"),
67                      parameterSupplier.get()));
68          } catch (RuntimeException e) {
69              Throwable cause = e.getCause();
70              if (cause != null) {
71                  if (cause instanceof IOException) {
72                      throw (IOException) cause;
73                  }
74                  if (cause instanceof URISyntaxException) {
75                      throw (URISyntaxException) cause;
76                  }
77              }
78              throw e;
79          }
80          ArrayList<MCRMediaSource> mediaSources = new ArrayList<>(4);
81          getDashStream()
82              .map(s -> new MCRMediaSource(s, MCRMediaSourceType.dash_stream))
83              .ifPresent(mediaSources::add);
84          userAgent.filter(MCRMediaSourceProvider::mayContainHLSStream).ifPresent(f -> getHLSStream()
85              .map(s -> new MCRMediaSource(s, MCRMediaSourceType.hls_stream))
86              .ifPresent(mediaSources::add));
87          getRTMPStream()
88              .map(s -> new MCRMediaSource(s, MCRMediaSourceType.rtmp_stream))
89              .ifPresent(mediaSources::add);
90          mediaSources.add(
91              new MCRMediaSource(getPseudoStream(MCRObjectID.getInstance(derivateId), path), MCRMediaSourceType.mp4));
92          this.sources = mediaSources;
93      }
94  
95      public static void updateWowzaSettings(String propertyName, Optional<String> oldValue,
96          Optional<String> newValue) {
97          switch (propertyName) {
98          case "MCR.Media.Wowza.BaseURL":
99              wowzaBaseURL = newValue;
100             break;
101         case "MCR.Media.Wowza.RTMPBaseURL":
102             wowzaRTMPBaseURL = newValue;
103             break;
104         case "MCR.Media.Wowza.HashParameter":
105             wowzaHashParameter = newValue.orElse("wowzatokenhash");
106             break;
107         default:
108             break;
109         }
110     }
111 
112     public List<MCRMediaSource> getSources() {
113         return Collections.unmodifiableList(sources);
114     }
115 
116     private Optional<String> toURL(MCRSecureTokenV2 token, Optional<String> baseURL, String suffix,
117         String hashParameterName) {
118         return baseURL.map(b -> {
119             try {
120                 return token.toURI(b, suffix, hashParameterName).toString();
121             } catch (URISyntaxException e) {
122                 throw new RuntimeException(e);
123             }
124         });
125     }
126 
127     private Optional<String> getDashStream() {
128         return wowzaToken.flatMap(w -> toURL(w, wowzaBaseURL, "/manifest.mpd", wowzaHashParameter));
129     }
130 
131     private Optional<String> getHLSStream() {
132         return wowzaToken.flatMap(w -> toURL(w, wowzaBaseURL, "/playlist.m3u8", wowzaHashParameter));
133     }
134 
135     private Optional<String> getRTMPStream() {
136         return wowzaToken.flatMap(w -> toURL(w, wowzaRTMPBaseURL, "", wowzaHashParameter));
137     }
138 
139     private String getPseudoStream(MCRObjectID derivate, String path) {
140         return MCRSecureTokenV2FilterConfig.getFileNodeServletSecured(derivate, path);
141     }
142 
143     private static String getContentPath(String derivateId, String path) {
144         try {
145             MCRPath mcrPath = MCRPath.getPath(derivateId, path);
146             MCRAbstractFileStore fileStore = (MCRAbstractFileStore) Files.getFileStore(mcrPath);
147             java.nio.file.Path absolutePath = fileStore.getPhysicalPath(mcrPath);
148             java.nio.file.Path relativePath = fileStore.getBaseDirectory().relativize(absolutePath);
149             LogManager.getLogger().info("{} -> {} -> {}", mcrPath, absolutePath, relativePath);
150             return relativePath.toString();
151         } catch (IOException e) {
152             throw new RuntimeException(e);
153         }
154     }
155 
156     private static boolean mayContainHLSStream(String userAgent) {
157         //Gecko on Android will not work if we submit HLS stream
158         return !(userAgent.contains("Android") && userAgent.contains("Gecko"));
159     }
160 }