Parcourir la source

解决串流,解决编辑上级平台却新建了的问题

panlinlin il y a 4 ans
Parent
commit
937e591430
32 fichiers modifiés avec 575 ajouts et 155 suppressions
  1. 2 2
      src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
  2. 12 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
  3. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java
  4. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java
  5. 16 6
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  6. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
  7. 4 4
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
  8. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/RegisterResponseProcessor.java
  9. 4 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
  10. 4 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
  11. 26 2
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
  12. 3 1
      src/main/java/com/genersoft/iot/vmp/service/IMediaService.java
  13. 12 3
      src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
  14. 11 2
      src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
  15. 1 1
      src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
  16. 6 2
      src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java
  17. 10 4
      src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
  18. 3 5
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java
  19. 1 2
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
  20. 1 1
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
  21. 3 3
      src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
  22. BIN
      src/main/resources/wvp.sqlite
  23. 2 0
      web_src/index.html
  24. 29 93
      web_src/src/components/Login.vue
  25. 1 0
      web_src/src/components/dialog/devicePlayer.vue
  26. 21 20
      web_src/src/components/dialog/platformEdit.vue
  27. 3 0
      web_src/static/css/font-awesome.min.css
  28. 396 0
      web_src/static/css/login.css
  29. BIN
      web_src/static/fonts/poppins/Poppins-Bold.ttf
  30. BIN
      web_src/static/fonts/poppins/Poppins-Medium.ttf
  31. BIN
      web_src/static/fonts/poppins/Poppins-Regular.ttf
  32. BIN
      web_src/static/fonts/poppins/Poppins-SemiBold.ttf

+ 2 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java

@@ -118,7 +118,7 @@ public class SipLayer implements SipListener {
 	 */
 	@Override
 	public void processRequest(RequestEvent evt) {
-		logger.debug(evt.getRequest().toString());
+//		logger.debug(evt.getRequest().toString());
 		// 由于jainsip是单线程程序,为提高性能并发处理
 		processThreadPool.execute(() -> {
 			if (processorFactory != null) {
@@ -130,7 +130,7 @@ public class SipLayer implements SipListener {
 	@Override
 	public void processResponse(ResponseEvent evt) {
 		Response response = evt.getResponse();
-		logger.debug(evt.getResponse().toString());
+//		logger.debug(evt.getResponse().toString());
 		int status = response.getStatusCode();
 		if (((status >= 200) && (status < 300)) || status == 401) { // Success!
 			ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);

+ 12 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java

@@ -2,6 +2,11 @@ package com.genersoft.iot.vmp.gb28181.bean;
 
 public class ParentPlatform {
 
+    /**
+     * id
+     */
+    private Integer id;
+
     /**
      * 是否启用
      */
@@ -99,6 +104,13 @@ public class ParentPlatform {
      */
     private int channelCount;
 
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
 
     public boolean isEnable() {
         return enable;

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java

@@ -49,7 +49,7 @@ public class PlatformKeepaliveExpireEventLister implements ApplicationListener<P
         if (logger.isDebugEnabled()) {
             logger.debug("平台心跳到期事件事件触发,平台国标ID:" + event.getPlatformGbID());
         }
-        ParentPlatform parentPlatform = storager.queryParentPlatById(event.getPlatformGbID());
+        ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformGbID());
         ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(event.getPlatformGbID());
         if (parentPlatformCatch == null) {
             return;

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java

@@ -35,7 +35,7 @@ public class PlatformNotRegisterEventLister implements ApplicationListener<Platf
 
         logger.debug("平台未注册事件触发,平台国标ID:" + event.getPlatformGbID());
 
-        ParentPlatform parentPlatform = storager.queryParentPlatById(event.getPlatformGbID());
+        ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformGbID());
         if (parentPlatform == null) {
             logger.debug("平台未注册事件触发,但平台已经删除!!! 平台国标ID:" + event.getPlatformGbID());
             return;

+ 16 - 6
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java

@@ -365,9 +365,14 @@ public class SIPCommander implements ISIPCommander {
 			// 添加订阅
 			JSONObject subscribeKey = new JSONObject();
 			subscribeKey.put("app", "rtp");
-			subscribeKey.put("id", streamId);
-
-			subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event);
+			subscribeKey.put("stream", streamId);
+			subscribeKey.put("regist", true);
+
+			subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, json->{
+				if (json.getJSONArray("tracks") == null) return;
+				event.response(json);
+				subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
+			});
 			//
 			StringBuffer content = new StringBuffer(200);
 			content.append("v=0\r\n");
@@ -465,9 +470,14 @@ public class SIPCommander implements ISIPCommander {
 			// 添加订阅
 			JSONObject subscribeKey = new JSONObject();
 			subscribeKey.put("app", "rtp");
-			subscribeKey.put("id", streamId);
-
-			subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event);
+			subscribeKey.put("stream", streamId);
+			subscribeKey.put("regist", true);
+
+			subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, json->{
+				if (json.getJSONArray("tracks") == null) return;
+				event.response(json);
+				subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
+			});
 
 			StringBuffer content = new StringBuffer(200);
 	        content.append("v=0\r\n");

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java

@@ -86,7 +86,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
 			}
 
 			// 查询请求方是否上级平台
-			ParentPlatform platform = storager.queryParentPlatById(requesterId);
+			ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
 			if (platform != null) {
 				// 查询平台下是否有该通道
 				DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);

+ 4 - 4
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java

@@ -238,7 +238,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
 					// 回复200 OK
 					responseAck(evt);
 					String sn = rootElement.element("SN").getText();
-					ParentPlatform parentPlatform = storager.queryParentPlatById(platformId);
+					ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId);
 					cmderFroPlatform.deviceStatusResponse(parentPlatform, sn, fromHeader.getTag());
 				}
 			} else {
@@ -303,7 +303,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
 					if (deviceId.equals(targetGBId)) {
 						// 远程启动本平台:需要在重新启动程序后先对SipStack解绑
 						logger.info("执行远程启动本平台命令");
-						ParentPlatform parentPlatform = storager.queryParentPlatById(platformId);
+						ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId);
 						cmderFroPlatform.unregister(parentPlatform, null, null);
 	
 						Thread restartThread = new Thread(new Runnable() {
@@ -463,7 +463,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
 					// 回复200 OK
 					responseAck(evt);
 					String sn = rootElement.element("SN").getText();
-					ParentPlatform parentPlatform = storager.queryParentPlatById(platformId);
+					ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId);
 					cmderFroPlatform.deviceInfoResponse(parentPlatform, sn, fromHeader.getTag());
 				}
 			} else {
@@ -517,7 +517,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
 			// if (deviceListElement == null) { // 存在DeviceList则为响应 catalog, 不存在DeviceList则为查询请求
 			if (name.equalsIgnoreCase("Query")) { // 区分是Response——查询响应,还是Query——查询请求
 				// TODO 后续将代码拆分
-				ParentPlatform parentPlatform = storager.queryParentPlatById(platformId);
+				ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId);
 				if (parentPlatform == null) {
 					response404Ack(evt);
 					return;

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/RegisterResponseProcessor.java

@@ -82,7 +82,7 @@ public class RegisterResponseProcessor implements ISIPResponseProcessor {
 			redisCatchStorage.delPlatformRegisterInfo(callId);
 			parentPlatform.setStatus(true);
 			// 取回Expires设置,避免注销过程中被置为0
-			ParentPlatform parentPlatformTmp = storager.queryParentPlatById(platformGBId);
+			ParentPlatform parentPlatformTmp = storager.queryParentPlatByServerGBId(platformGBId);
 			String expires = parentPlatformTmp.getExpires();
 			parentPlatform.setExpires(expires);
 			storager.updateParentPlatform(parentPlatform);

+ 4 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java

@@ -240,6 +240,10 @@ public class ZLMHttpHookListener {
 		if (logger.isDebugEnabled()) {
 			logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString());
 		}
+
+		ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, json);
+		if (subscribe != null) subscribe.response(json);
+
 		// 流消失移除redis play
 		String app = json.getString("app");
 		String streamId = json.getString("stream");

+ 4 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java

@@ -163,6 +163,10 @@ public class ZLMRESTfulUtils {
         return sendPost("closeRtpServer",param, null);
     }
 
+    public JSONObject listRtpServer() {
+        return sendPost("listRtpServer",null, null);
+    }
+
     public JSONObject startSendRtp(Map<String, Object> param) {
         return sendPost("startSendRtp",param, null);
     }

+ 26 - 2
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java

@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.media.zlm;
 
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
 import com.genersoft.iot.vmp.gb28181.session.SsrcUtil;
@@ -27,7 +28,28 @@ public class ZLMRTPServerFactory {
 
     private int currentPort = 0;
 
+    private Map<String, Integer> currentStreams = null;
+
     public int createRTPServer(String streamId) {
+        if (currentStreams == null) {
+            currentStreams = new HashMap<>();
+            JSONObject jsonObject = zlmresTfulUtils.listRtpServer();
+            JSONArray data = jsonObject.getJSONArray("data");
+            if (data != null) {
+                for (int i = 0; i < data.size(); i++) {
+                    JSONObject dataItem = data.getJSONObject(i);
+                    currentStreams.put(dataItem.getString("stream_id"), dataItem.getInteger("port"));
+                }
+            }
+        }
+        // 已经在推流
+        if (currentStreams.get(streamId) != null) {
+            Map<String, Object> closeRtpServerParam = new HashMap<>();
+            closeRtpServerParam.put("stream_id", streamId);
+            zlmresTfulUtils.closeRtpServer(closeRtpServerParam);
+            currentStreams.remove(streamId);
+        }
+
         Map<String, Object> param = new HashMap<>();
         int result = -1;
         int newPort = getPortFromUdpPortRange();
@@ -35,14 +57,16 @@ public class ZLMRTPServerFactory {
         param.put("enable_tcp", 1);
         param.put("stream_id", streamId);
         JSONObject jsonObject = zlmresTfulUtils.openRtpServer(param);
-        System.out.println(jsonObject);
 
         if (jsonObject != null) {
             switch (jsonObject.getInteger("code")){
                 case 0:
                     result= newPort;
                     break;
-                case -300: // id已经存在
+                case -300: // id已经存在, 可能已经在其他端口推流
+                    Map<String, Object> closeRtpServerParam = new HashMap<>();
+                    closeRtpServerParam.put("stream_id", streamId);
+                    zlmresTfulUtils.closeRtpServer(closeRtpServerParam);
                     result = newPort;
                     break;
                 case -400: // 端口占用

+ 3 - 1
src/main/java/com/genersoft/iot/vmp/service/IMediaService.java

@@ -1,5 +1,7 @@
 package com.genersoft.iot.vmp.service;
 
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 
 /**
@@ -21,6 +23,6 @@ public interface IMediaService {
      * @param stream
      * @return
      */
-    StreamInfo getStreamInfoByAppAndStream(String app, String stream);
+    StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks);
 
 }

+ 12 - 3
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java

@@ -1,5 +1,7 @@
 package com.genersoft.iot.vmp.service.impl;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.MediaServerConfig;
@@ -25,7 +27,7 @@ public class MediaServiceImpl implements IMediaService {
 
 
     @Override
-    public StreamInfo getStreamInfoByAppAndStream(String app, String stream) {
+    public StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks) {
         MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
         StreamInfo streamInfoResult = new StreamInfo();
         streamInfoResult.setStreamId(stream);
@@ -41,7 +43,7 @@ public class MediaServiceImpl implements IMediaService {
         streamInfoResult.setTs(String.format("http://%s:%s/%s/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app,  stream));
         streamInfoResult.setWs_ts(String.format("ws://%s:%s/%s/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app,  stream));
         streamInfoResult.setRtc(String.format("http://%s:%s/index/api/webrtc?app=%s&stream=%s&type=play", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app,  stream));
-
+        streamInfoResult.setTracks(tracks);
         return streamInfoResult;
     }
 
@@ -50,7 +52,14 @@ public class MediaServiceImpl implements IMediaService {
         StreamInfo streamInfo = null;
         JSONObject mediaList = zlmresTfulUtils.getMediaList(app, stream);
         if (mediaList != null) {
-            streamInfo = getStreamInfoByAppAndStream(app, stream);
+            if (mediaList.getInteger("code") == 0) {
+                JSONArray data = mediaList.getJSONArray("data");
+                if (data == null) return null;
+                JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class);
+                JSONArray tracks = mediaJSON.getJSONArray("tracks");
+                streamInfo = getStreamInfoByAppAndStream(app, stream, tracks);
+            }
+
         }
         return streamInfo;
     }

+ 11 - 2
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java

@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.service.impl;
 
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
@@ -83,6 +84,13 @@ public class PlayServiceImpl implements IPlayService {
             });
         } else {
             String streamId = streamInfo.getStreamId();
+            if (streamId == null) {
+                RequestMessage msg = new RequestMessage();
+                msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
+                msg.setData(String.format("点播失败, redis缓存streamId等于null"));
+                resultHolder.invokeResult(msg);
+                return playResult;
+            }
             JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
             if (rtpInfo != null && rtpInfo.getBoolean("exist")) {
                 RequestMessage msg = new RequestMessage();
@@ -150,8 +158,9 @@ public class PlayServiceImpl implements IPlayService {
     }
 
     public StreamInfo onPublishHandler(JSONObject resonse, String deviceId, String channelId, String uuid) {
-        String streamId = resonse.getString("id");
-        StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream("rtp", streamId);
+        String streamId = resonse.getString("stream");
+        JSONArray tracks = resonse.getJSONArray("tracks");
+        StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream("rtp", streamId, tracks);
         streamInfo.setDeviceID(deviceId);
         streamInfo.setChannelId(channelId);
         return streamInfo;

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java

@@ -194,7 +194,7 @@ public interface IVideoManagerStorager {
 	 * @param platformGbId
 	 * @return
 	 */
-	ParentPlatform queryParentPlatById(String platformGbId);
+	ParentPlatform queryParentPlatByServerGBId(String platformGbId);
 
 	/**
 	 * 所有平台离线

+ 6 - 2
src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java

@@ -25,6 +25,7 @@ public interface ParentPlatformMapper {
             "SET enable=#{enable}, " +
             "name=#{name}," +
             "deviceGBId=#{deviceGBId}," +
+            "serverGBId=#{serverGBId}, " +
             "serverGBDomain=#{serverGBDomain}, " +
             "serverIP=#{serverIP}," +
             "serverPort=#{serverPort}, " +
@@ -39,7 +40,7 @@ public interface ParentPlatformMapper {
             "ptz=#{ptz}, " +
             "rtcp=#{rtcp}, " +
             "status=#{status} " +
-            "WHERE serverGBId=#{serverGBId}")
+            "WHERE id=#{id}")
     int updateParentPlatform(ParentPlatform parentPlatform);
 
     @Delete("DELETE FROM parent_platform WHERE serverGBId=#{serverGBId}")
@@ -52,7 +53,10 @@ public interface ParentPlatformMapper {
     List<ParentPlatform> getEnableParentPlatformList(boolean enable);
 
     @Select("SELECT * FROM parent_platform WHERE serverGBId=#{platformGbId}")
-    ParentPlatform getParentPlatById(String platformGbId);
+    ParentPlatform getParentPlatByServerGBId(String platformGbId);
+
+    @Select("SELECT * FROM parent_platform WHERE id=#{id}")
+    ParentPlatform getParentPlatById(int id);
 
     @Update("UPDATE parent_platform SET status=false" )
     void outlineForAllParentPlatform();

+ 10 - 4
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java

@@ -271,15 +271,21 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
 	public boolean updateParentPlatform(ParentPlatform parentPlatform) {
 		int result = 0;
 		ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); // .getDeviceGBId());
-		if ( platformMapper.getParentPlatById(parentPlatform.getServerGBId()) == null) {
+		if (parentPlatform.getId() == null ) {
 			result = platformMapper.addParentPlatform(parentPlatform);
-
 			if (parentPlatformCatch == null) {
 				parentPlatformCatch = new ParentPlatformCatch();
 				parentPlatformCatch.setParentPlatform(parentPlatform);
 				parentPlatformCatch.setId(parentPlatform.getServerGBId());
 			}
 		}else {
+			if (parentPlatformCatch == null) { // serverGBId 已变化
+				ParentPlatform parentPlatById = platformMapper.getParentPlatById(parentPlatform.getId());
+				// 使用旧的查出缓存ID
+				parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatById.getServerGBId());
+				parentPlatformCatch.setId(parentPlatform.getServerGBId());
+				redisCatchStorage.delPlatformCatchInfo(parentPlatById.getServerGBId());
+			}
 			result = platformMapper.updateParentPlatform(parentPlatform);
 		}
 		// 更新缓存
@@ -305,8 +311,8 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
 	}
 
 	@Override
-	public ParentPlatform queryParentPlatById(String platformGbId) {
-		return platformMapper.getParentPlatById(platformGbId);
+	public ParentPlatform queryParentPlatByServerGBId(String platformGbId) {
+		return platformMapper.getParentPlatByServerGBId(platformGbId);
 	}
 
 	@Override

+ 3 - 5
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java

@@ -111,7 +111,7 @@ public class PlatformController {
         // TODO 检查是否已经存在,且注册成功, 如果注册成功,需要先注销之前再,修改并注册
 
         // ParentPlatform parentPlatformOld = storager.queryParentPlatById(parentPlatform.getDeviceGBId());
-        ParentPlatform parentPlatformOld = storager.queryParentPlatById(parentPlatform.getServerGBId());
+        ParentPlatform parentPlatformOld = storager.queryParentPlatByServerGBId(parentPlatform.getServerGBId());
 
         boolean updateResult = storager.updateParentPlatform(parentPlatform);
 
@@ -123,8 +123,6 @@ public class PlatformController {
             } else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()){ // 关闭启用时注销
                 commanderForPlatform.unregister(parentPlatform, null, null);
             }
-
-
             return new ResponseEntity<>("success", HttpStatus.OK);
         } else {
             return new ResponseEntity<>("fail", HttpStatus.OK);
@@ -151,7 +149,7 @@ public class PlatformController {
         ){
             return new ResponseEntity<>("missing parameters", HttpStatus.BAD_REQUEST);
         }
-        ParentPlatform parentPlatform = storager.queryParentPlatById(serverGBId);
+        ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId);
         if (parentPlatform == null) return new ResponseEntity<>("fail", HttpStatus.OK);
         // 发送离线消息,无论是否成功都删除缓存
         commanderForPlatform.unregister(parentPlatform, (event -> {
@@ -192,7 +190,7 @@ public class PlatformController {
         if (logger.isDebugEnabled()) {
             logger.debug("查询上级平台是否存在API调用:" + serverGBId);
         }
-        ParentPlatform parentPlatform = storager.queryParentPlatById(serverGBId);
+        ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId);
         return new ResponseEntity<>(String.valueOf(parentPlatform != null), HttpStatus.OK);
     }
 

+ 1 - 2
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java

@@ -73,7 +73,6 @@ public class PlayController {
 	public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId,
 													   @PathVariable String channelId) {
 
-
 		PlayResult playResult = playService.play(deviceId, channelId, null, null);
 
 		// 超时处理
@@ -181,7 +180,7 @@ public class PlayController {
 				JSONObject data = jsonObject.getJSONObject("data");
 				if (data != null) {
 				   	result.put("key", data.getString("key"));
-					StreamInfo streamInfoResult = mediaService.getStreamInfoByAppAndStream("convert", streamId);
+					StreamInfo streamInfoResult = mediaService.getStreamInfoByAppAndStreamWithCheck("convert", streamId);
 					result.put("data", streamInfoResult);
 				}
 			}else {

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java

@@ -71,7 +71,7 @@ public class PlaybackController {
 			logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
 		}
 		UUID uuid = UUID.randomUUID();
-		DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>();
+		DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(30000L);
 		// 超时处理
 		result.onTimeout(()->{
 			logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId));

+ 3 - 3
src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java

@@ -21,10 +21,10 @@ import javax.security.sasl.AuthenticationException;
 public class UserController {
 
     @Autowired
-    AuthenticationManager authenticationManager;
+    private AuthenticationManager authenticationManager;
 
     @Autowired
-    IUserService userService;
+    private IUserService userService;
 
     @ApiOperation("登录")
     @ApiImplicitParams({
@@ -33,7 +33,7 @@ public class UserController {
     })
     @GetMapping("/login")
     public String login(String username, String password){
-        LoginUser user = null;
+        LoginUser user;
         try {
             user = SecurityUtils.login(username, password, authenticationManager);
         } catch (AuthenticationException e) {

BIN
src/main/resources/wvp.sqlite


+ 2 - 0
web_src/index.html

@@ -4,6 +4,8 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     <title>国标28181</title>
+    <link rel="stylesheet" type="text/css" href="/static/css/font-awesome.min.css">
+    <link rel="stylesheet" type="text/css" href="/static/css/login.css">
   </head>
   <body>
     <script type="text/javascript" src="./js/EasyWasmPlayer.js"></script>

+ 29 - 93
web_src/src/components/Login.vue

@@ -1,19 +1,35 @@
 <template>
 <div class="login" id="login">
-    <a href="javascript:;" class="log-close"><i class="icons close"></i></a>
-    <div class="log-bg">
-        <div class="log-cloud cloud1"></div>
-        <div class="log-cloud cloud2"></div>
-        <div class="log-cloud cloud3"></div>
-        <div class="log-cloud cloud4"></div>
+  <div class="limiter">
+    <div class="container-login100">
+      <div class="wrap-login100">
+					<span class="login100-form-title p-b-26">WVP视频平台</span>
+          <span class="login100-form-title p-b-48">
+						<i class="fa fa-video-camera"></i>
+					</span>
 
-        <div class="log-logo">Welcome!</div>
-        <div class="log-text"></div>
-    </div>
-    <div class="log-email" v-loading="isLoging" >
-        <input type="text" placeholder="用户名" :class="'log-input' + (username==''?' log-input-empty':'')" v-model="username"><input type="password" placeholder="密码" :class="'log-input' + (password==''?' log-input-empty':'')"  v-model="password">
-        <a href="javascript:;" class="log-btn" @click="login" >登录</a>
+          <div class="wrap-input100 validate-input" data-validate = "Valid email is: a@b.c">
+            <input :class="'input100 ' + (username==''?'':'has-val')" type="text" v-model="username" name="username">
+            <span class="focus-input100" data-placeholder="用户名"></span>
+          </div>
+
+          <div class="wrap-input100 validate-input" data-validate="Enter password">
+						<span class="btn-show-pass">
+							<i :class="'fa ' + (!showPassword?'fa-eye':'fa-eye-slash')" @click="showPassword = !showPassword"></i>
+						</span>
+            <input :class="'input100 ' + (password==''?'':'has-val')" :type="(!showPassword?'password':'text')" v-model="password" name="password">
+            <span class="focus-input100" data-placeholder="密码"></span>
+          </div>
+
+          <div class="container-login100-form-btn">
+            <div class="wrap-login100-form-btn">
+              <div class="login100-form-bgbtn"></div>
+              <button class="login100-form-btn" @click="login">登录</button>
+            </div>
+          </div>
+      </div>
     </div>
+  </div>
 </div>
 </template>
 
@@ -24,6 +40,7 @@ export default {
   data(){
   	return {
       isLoging: false,
+      showPassword: false,
   		username: '',
   		password: ''
   	}
@@ -105,84 +122,3 @@ export default {
 }
 </script>
 
-<style scoped>
-.login{position: fixed; overflow: hidden;left: 50%; margin-left: -250px; top:50%; margin-top: -350px; width: 500px; min-height: 555px; z-index: 10; right: 140px; background: #fff;-webkit-border-radius: 5px;
--moz-border-radius: 5px;
--ms-border-radius: 5px;
--o-border-radius: 5px;
-border-radius: 5px; -webkit-box-shadow:  0px 3px 16px -5px #070707; box-shadow:  0px 3px 16px -5px #070707}
-.log-close{display: block; position: absolute; top:12px; right: 12px; opacity: 1;}
-.log-close:hover .icons{transform: rotate(180deg);}
-.log-close .icons{opacity: 1; transition: all .3s}
-.log-cloud{background-image: url(../assets/login-cloud.png); width: 63px ;height: 40px; position: absolute; z-index: 1}
-.login .cloud1{top:21px; left: -30px; transform: scale(.6); animation: cloud1 20s linear infinite;}
-.login .cloud2{top:87px; right: 20px; animation: cloud2 19s linear infinite;}
-.login .cloud3{top:160px; left: 5px;transform: scale(.8);animation: cloud3 21s linear infinite;}
-.login .cloud4{top:150px; left: -40px;transform: scale(.4);animation: cloud4 19s linear infinite;}
-.log-bg{background: url(../assets/login-bg.jpg); width: 100%; height: 312px; overflow: hidden;}
-.log-logo{height: 80px; margin: 120px auto 25px; text-align: center; color: #1fcab3; font-weight: bold; font-size: 40px;}
-.log-text{color: #57d4c3; font-size: 13px; text-align: center; margin: 0 auto;}
-.log-logo,.log-text{z-index: 2}
-.icons{background:url(../assets/icons.png) no-repeat; display: inline-block;}
-.close{height:16px;width:16px;background-position:-13px 0;}
-.login-email{height:17px;width:29px;background-position:-117px 0;}
-
-.log-btns{padding: 15px 0; margin: 0 auto;}
-.log-btn{width:402px; display: block; text-align: left; line-height: 50px;margin:0 auto 15px; height:50px; color:#fff; font-size:13px;-webkit-border-radius: 5px; background-color: #3B5999;
--moz-border-radius: 5px;
--ms-border-radius: 5px;
--o-border-radius: 5px;
-border-radius: 5px;
-position: relative;}
-.log-btn.tw{background-color: #13B4E9}
-.log-btn.email{background-color: #50E3CE}
-.log-btn:hover,.log-btn:focus{color: #fff; opacity: .8;}
-
-.log-email{text-align: center; margin-top: 20px;}
-.log-email .log-btn{background-color: #50E3CE;text-align: center;}
-.log-input-empty{border: 1px solid #f37474 !important;}
-.isloading{background: #d6d6d6}
-.log-btn .icons{margin-left: 30px; vertical-align: middle;}
-.log-btn .text{left: 95px; line-height: 50px; text-align: left; position: absolute;}
-.log-input{width: 370px;overflow: hidden; padding: 0 15px;font-size: 13px; border: 1px solid #EBEBEB; margin:0 auto 15px; height: 48px; line-height: 48px; -webkit-border-radius: 5px;
--moz-border-radius: 5px;
--ms-border-radius: 5px;
--o-border-radius: 5px;
-border-radius: 5px;}
-.log-input.warn{border: 1px solid #f88787}
-
- @-webkit-keyframes cloud1 {
-    0%{left: 200px}
-    100%{left:-130px;}
-}
-@keyframes cloud1{
-    0%{left: 200px}
-    100%{left:-130px;}
-}
-
- @-webkit-keyframes cloud2 {
-    0%{left:500px;}
-    100%{left:-90px;}
-}
-@keyframes cloud2{
-    0%{left:500px;}
-    100%{left:-90px;}
-}
-
-@-webkit-keyframes cloud3 {
-    0%{left:620px;}
-    100%{left:-70px;}
-}
-@keyframes cloud3{
-    0%{left:620px;}
-    100%{left:-70px;}
-}@-webkit-keyframes cloud4 {
-    0%{left:100px;}
-    100%{left:-70px;}
-}
-@keyframes cloud4{
-    0%{left:100px;}
-    100%{left:-70px;}
-}
-
-</style>

+ 1 - 0
web_src/src/components/dialog/devicePlayer.vue

@@ -138,6 +138,7 @@
 
 <script>
 // import player from '../dialog/rtcPlayer.vue'
+// import LivePlayer from '@liveqing/liveplayer'
 import player from '../dialog/easyPlayer.vue'
 export default {
     name: 'devicePlayer',

+ 21 - 20
web_src/src/components/dialog/platformEdit.vue

@@ -122,19 +122,20 @@ export default {
       onSubmit_text: "立即创建",
 
       platform: {
+        id: null,
         enable: true,
         ptz: true,
         rtcp: false,
-        name: "测试001",
-        serverGBId: "34020000002000000001",
-        serverGBDomain: "3402000000",
-        serverIP: "192.168.1.141",
-        serverPort: "5060",
-        deviceGBId: "34020000001320001101",
-        deviceIp: "192.168.1.20",
-        devicePort: "5060",
-        username: "34020000001320001101",
-        password: "12345678",
+        name: null,
+        serverGBId: null,
+        serverGBDomain: null,
+        serverIP: null,
+        serverPort: null,
+        deviceGBId: null,
+        deviceIp: null,
+        devicePort: null,
+        username: null,
+        password: null,
         expires: 300,
         keepTimeout: 60,
         transport: "UDP",
@@ -163,27 +164,27 @@ export default {
   methods: {
     openDialog: function (platform, callback) {
       var that = this;
-      this.$axios({
-                method: 'get',
-                url:`/api/platform/server_config`
-      }).then(function (res) {
+      if (platform == null) {
+        this.onSubmit_text = "立即创建";
+        this.$axios({
+          method: 'get',
+          url:`/api/platform/server_config`
+        }).then(function (res) {
           console.log(res);
           that.platform.deviceGBId = res.data.username;
           that.platform.deviceIp = res.data.deviceIp;
           that.platform.devicePort = res.data.devicePort;
           that.platform.username = res.data.username;
           that.platform.password = res.data.password;
-      }).catch(function (error) {
+        }).catch(function (error) {
           console.log(error);
         });
-      this.showDialog = true;
-      this.listChangeCallback = callback;
-      if (platform != null) {
+      }else {
         this.platform = platform;
         this.onSubmit_text = "保存";
-      } else {
-        this.onSubmit_text = "立即创建";
       }
+      this.showDialog = true;
+      this.listChangeCallback = callback;
     },
     onSubmit: function () {
       console.log("onSubmit");

Fichier diff supprimé car celui-ci est trop grand
+ 3 - 0
web_src/static/css/font-awesome.min.css


+ 396 - 0
web_src/static/css/login.css

@@ -0,0 +1,396 @@
+
+
+
+
+/*//////////////////////////////////////////////////////////////////
+[ FONT ]*/
+
+@font-face {
+  font-family: Poppins-Regular;
+  src: url('../fonts/poppins/Poppins-Regular.ttf');
+}
+
+@font-face {
+  font-family: Poppins-Medium;
+  src: url('../fonts/poppins/Poppins-Medium.ttf');
+}
+
+@font-face {
+  font-family: Poppins-Bold;
+  src: url('../fonts/poppins/Poppins-Bold.ttf');
+}
+
+@font-face {
+  font-family: Poppins-SemiBold;
+  src: url('../fonts/poppins/Poppins-SemiBold.ttf');
+}
+
+.limiter {
+  width: 100%;
+  margin: 0 auto;
+  font-family: Poppins-Regular, sans-serif;
+
+}
+
+.container-login100 {
+  width: 100%;
+  min-height: 100vh;
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -moz-box;
+  display: -ms-flexbox;
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+  align-items: center;
+  background: #f2f2f2;
+}
+
+.wrap-login100 {
+  background: #fff;
+  border-radius: 10px;
+  overflow: hidden;
+  padding: 77px 55px 33px 55px;
+
+  box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1);
+  -moz-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1);
+  -webkit-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1);
+  -o-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1);
+  -ms-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1);
+}
+
+
+/*------------------------------------------------------------------
+[ Form ]*/
+
+.login100-form {
+  width: 100%;
+}
+
+.login100-form-title {
+  display: block;
+  font-family: Poppins-Bold;
+  font-size: 30px;
+  color: #333333;
+  line-height: 1.2;
+  text-align: center;
+
+}
+.login100-form-title i {
+  font-size: 60px;
+}
+
+/*------------------------------------------------------------------
+[ Input ]*/
+
+.wrap-input100 {
+  width: 100%;
+  position: relative;
+  border-bottom: 2px solid #adadad;
+  margin-bottom: 37px;
+}
+
+.input100 {
+  font-family: Poppins-Regular;
+  font-size: 15px;
+  color: #555555;
+  line-height: 1.2;
+
+  display: block;
+  width: 100%;
+  height: 45px;
+  background: transparent;
+  padding: 0 5px;
+  outline: none;
+  border: none;
+}
+.input100::-webkit-input-placeholder { color: #adadad;}
+.input100:-moz-placeholder { color: #adadad;}
+.input100::-moz-placeholder { color: #adadad;}
+.input100:-ms-input-placeholder { color: #adadad;}
+
+/*---------------------------------------------*/
+.focus-input100 {
+  position: absolute;
+  display: block;
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  pointer-events: none;
+  color:transparent;
+}
+
+.focus-input100::before {
+  content: "";
+  display: block;
+  position: absolute;
+  bottom: -2px;
+  left: 0;
+  width: 0;
+  height: 2px;
+
+  -webkit-transition: all 0.4s;
+  -o-transition: all 0.4s;
+  -moz-transition: all 0.4s;
+  transition: all 0.4s;
+
+  background: #6a7dfe;
+  background: -webkit-linear-gradient(left, #21d4fd, #b721ff);
+  background: -o-linear-gradient(left, #21d4fd, #b721ff);
+  background: -moz-linear-gradient(left, #21d4fd, #b721ff);
+  background: linear-gradient(left, #21d4fd, #b721ff);
+}
+
+.focus-input100::after {
+  font-family: Poppins-Regular;
+  font-size: 15px;
+  color: #999999;
+  line-height: 1.2;
+
+  content: attr(data-placeholder);
+  display: block;
+  width: 100%;
+  position: absolute;
+  top: 16px;
+  left: 0px;
+  padding-left: 5px;
+
+  -webkit-transition: all 0.4s;
+  -o-transition: all 0.4s;
+  -moz-transition: all 0.4s;
+  transition: all 0.4s;
+}
+
+.input100:focus + .focus-input100::after {
+  top: -15px;
+}
+
+.input100:focus + .focus-input100::before {
+  width: 100%;
+}
+
+.has-val.input100 + .focus-input100::after {
+  top: -15px;
+}
+
+.has-val.input100 + .focus-input100::before {
+  width: 100%;
+}
+
+/*---------------------------------------------*/
+.btn-show-pass {
+  font-size: 15px;
+  color: #999999;
+
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -moz-box;
+  display: -ms-flexbox;
+  display: flex;
+  align-items: center;
+  position: absolute;
+  height: 100%;
+  top: 0;
+  right: 0;
+  padding-right: 5px;
+  cursor: pointer;
+  -webkit-transition: all 0.4s;
+  -o-transition: all 0.4s;
+  -moz-transition: all 0.4s;
+  transition: all 0.4s;
+}
+
+.btn-show-pass:hover {
+  color: #6a7dfe;
+  color: -webkit-linear-gradient(left, #21d4fd, #b721ff);
+  color: -o-linear-gradient(left, #21d4fd, #b721ff);
+  color: -moz-linear-gradient(left, #21d4fd, #b721ff);
+  color: linear-gradient(left, #21d4fd, #b721ff);
+}
+
+.btn-show-pass.active {
+  color: #6a7dfe;
+  color: -webkit-linear-gradient(left, #21d4fd, #b721ff);
+  color: -o-linear-gradient(left, #21d4fd, #b721ff);
+  color: -moz-linear-gradient(left, #21d4fd, #b721ff);
+  color: linear-gradient(left, #21d4fd, #b721ff);
+}
+
+
+
+/*------------------------------------------------------------------
+[ Button ]*/
+.container-login100-form-btn {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -moz-box;
+  display: -ms-flexbox;
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+  padding-top: 13px;
+}
+
+.wrap-login100-form-btn {
+  width: 100%;
+  display: block;
+  position: relative;
+  z-index: 1;
+  border-radius: 25px;
+  overflow: hidden;
+  margin: 0 auto;
+}
+
+.login100-form-bgbtn {
+  position: absolute;
+  z-index: -1;
+  width: 300%;
+  height: 100%;
+  background: #a64bf4;
+  background: -webkit-linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff);
+  background: -o-linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff);
+  background: -moz-linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff);
+  background: linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff);
+  top: 0;
+  left: -100%;
+
+  -webkit-transition: all 0.4s;
+  -o-transition: all 0.4s;
+  -moz-transition: all 0.4s;
+  transition: all 0.4s;
+}
+
+.login100-form-btn {
+  font-family: Poppins-Medium;
+  font-size: 15px;
+  color: #fff;
+  line-height: 1.2;
+  text-transform: uppercase;
+
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -moz-box;
+  display: -ms-flexbox;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 0 20px;
+  width: 100%;
+  height: 50px;
+  outline: none !important;
+  border: none;
+  background: transparent;
+}
+
+.login100-form-btn:hover {
+  cursor: pointer;
+}
+
+.wrap-login100-form-btn:hover .login100-form-bgbtn {
+  left: 0;
+}
+
+
+/*------------------------------------------------------------------
+[ Responsive ]*/
+
+@media (max-width: 576px) {
+  .wrap-login100 {
+    padding: 77px 15px 33px 15px;
+  }
+}
+
+
+
+/*------------------------------------------------------------------
+[ Alert validate ]*/
+
+.validate-input {
+  position: relative;
+}
+
+.alert-validate::before {
+  content: attr(data-validate);
+  position: absolute;
+  max-width: 70%;
+  background-color: #fff;
+  border: 1px solid #c80000;
+  border-radius: 2px;
+  padding: 4px 25px 4px 10px;
+  top: 50%;
+  -webkit-transform: translateY(-50%);
+  -moz-transform: translateY(-50%);
+  -ms-transform: translateY(-50%);
+  -o-transform: translateY(-50%);
+  transform: translateY(-50%);
+  right: 0px;
+  pointer-events: none;
+
+  font-family: Poppins-Regular;
+  color: #c80000;
+  font-size: 13px;
+  line-height: 1.4;
+  text-align: left;
+
+  visibility: hidden;
+  opacity: 0;
+
+  -webkit-transition: opacity 0.4s;
+  -o-transition: opacity 0.4s;
+  -moz-transition: opacity 0.4s;
+  transition: opacity 0.4s;
+}
+
+.alert-validate::after {
+  content: "\f06a";
+  font-family: FontAwesome;
+  font-size: 16px;
+  color: #c80000;
+
+  display: block;
+  position: absolute;
+  background-color: #fff;
+  top: 50%;
+  -webkit-transform: translateY(-50%);
+  -moz-transform: translateY(-50%);
+  -ms-transform: translateY(-50%);
+  -o-transform: translateY(-50%);
+  transform: translateY(-50%);
+  right: 5px;
+}
+
+.alert-validate:hover:before {
+  visibility: visible;
+  opacity: 1;
+}
+
+@media (max-width: 992px) {
+  .alert-validate::before {
+    visibility: visible;
+    opacity: 1;
+  }
+}
+
+/** util **/
+.p-b-26 {padding-bottom: 26px;}
+.p-b-48 {padding-bottom: 48px;}
+.p-t-115 {padding-top: 115px;}
+
+/**font**/
+/* .zmdi {
+  display: inline-block;
+  font: normal normal normal 14px/1 'Material-Design-Iconic-Font';
+      font-size: 14px;
+  font-size: inherit;
+  text-rendering: auto;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+.zmdi-font::before {
+  content: '\f16a';
+}
+.login100-form-title i {
+  font-size: 60px;
+} */
+

BIN
web_src/static/fonts/poppins/Poppins-Bold.ttf


BIN
web_src/static/fonts/poppins/Poppins-Medium.ttf


BIN
web_src/static/fonts/poppins/Poppins-Regular.ttf


BIN
web_src/static/fonts/poppins/Poppins-SemiBold.ttf


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff