Pārlūkot izejas kodu

add test env and alert()

ming 3 gadi atpakaļ
vecāks
revīzija
a59185108d
4 mainītis faili ar 576 papildinājumiem un 5 dzēšanām
  1. 190 0
      index3.html
  2. 191 4
      main.py
  3. 1 1
      script_msg.js
  4. 194 0
      script_msg2.js

+ 190 - 0
index3.html

@@ -0,0 +1,190 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>AI ANCHOR GO</title>
+  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
+  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp"
+    crossorigin="anonymous">
+  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
+  <link rel="stylesheet" href="style.css">
+  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
+  <style>
+    body {
+      font-family: "Lato", sans-serif;
+    }
+    
+    .sidenav {
+      height: 100%;
+      width: 0;
+      position: fixed;
+      z-index: 1;
+      top: 0;
+      left: 0;
+      background-color: #111;
+      overflow-x: hidden;
+      transition: 0.5s;
+      padding-top: 60px;
+    }
+    
+    .sidenav a {
+      padding: 8px 8px 8px 32px;
+      text-decoration: none;
+      font-size: 25px;
+      color: #818181;
+      display: block;
+      transition: 0.3s;
+    }
+    
+    .sidenav a:hover {
+      color: #f1f1f1;
+    }
+    
+    .sidenav .closebtn {
+      position: absolute;
+      top: 0;
+      right: 25px;
+      font-size: 36px;
+      margin-left: 50px;
+    }
+    
+    @media screen and (max-height: 450px) {
+      .sidenav {padding-top: 15px;}
+      .sidenav a {font-size: 18px;}
+    }
+    </style>
+</head>
+<body>
+  <div class="container">
+    <div id="mySidenav" class="sidenav">
+      <a href="javascript:void(0)" class="closebtn" onclick="closeNav()">&times;</a>
+    </div>
+    <form action="/step_questions/submit" method="post" id="msform">
+      <span style="font-size:30px;cursor:pointer" onclick="openNav()">&#9776; 過去紀錄</span>
+      <!-- fieldsets -->
+      <fieldset>
+        <h2 class="fs-title">AI ANCHOR</h2>
+        <h3  class="fs-subtitle">標題</h3>
+        <input id=title type="text" name='t1' class='title_new' value="" placeholder="1" /> <br/>
+        <h3 class="fs-subtitle">台詞</h3>
+        <input type="text" name='t1' class='txtsrc1' value="" placeholder="1" /> <br/>
+        <input type="text" name='t2' class='txtsrc2' value="" placeholder="2" /><br/>
+        <input type="text" name='t3' class='txtsrc3' value="" placeholder="3" /><br/>
+        <input type="text" name='t4'  class='txtsrc4' value="" placeholder="4" /><br/>
+        <input type="text" name='t5' class='txtsrc5' value="" placeholder="5" /><br/>
+        <input type="text" name='t6' class='txtsrc6' value="" placeholder="6" /><br/>
+        <input type="text" name='t7' class='txtsrc7' value="" placeholder="7" /><br/>
+        <input type="text" name='t8' class='txtsrc8' value="" placeholder="8" /><br/>
+        <input type="text" name='t9' class='txtsrc9' value="" placeholder="9" /><br/>
+        <input type="text" name='t10' class='txtsrc10' value="" placeholder="10" /><br/>
+        <h3 class="fs-subtitle">影像連結</h3>
+        <input type="text" name='m1' class='imgsrc1' value="" placeholder="1" /> <br/>
+        <input type="text" name='m2' class='imgsrc2' value="" placeholder="2" /><br/>
+        <input type="text" name='m3' class='imgsrc3' value="" placeholder="3" /><br/>
+        <input type="text" name='m4' class='imgsrc4' value="" placeholder="4" /><br/>
+        <input type="text" name='m5' class='imgsrc5' value="" placeholder="5" /><br/>
+        <input type="text" name='m6' class='imgsrc6' value="" placeholder="6" /><br/>
+        <input type="text" name='m7' class='imgsrc7' value="" placeholder="7" /><br/>
+        <input type="text" name='m8' class='imgsrc8' value="" placeholder="8" /><br/>
+        <input type="text" name='m9' class='imgsrc9' value="" placeholder="9" /><br/>
+        <input type="text" name='m10' class='imgsrc10' value="" placeholder="10" /><br/>
+        <input id="checker" type="button" name="next" class="next action-button" value="送出" />
+        <div>
+        <h3 class="fs-subtitle">處理進度</h3>
+        <div id="myProgress">
+          
+          <div id="myBar">0%</div>
+        </div>
+      </fieldset>
+    </form>
+        
+    
+  </div>
+  
+  <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
+  <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js'></script>
+  <script src="script_msg2.js"></script>
+  <script>
+    var loaded_data = ''
+    function openNav() {
+      document.getElementById("mySidenav").style.width = "250px";
+  
+        $.get("http://www.choozmo.com:8888/history_input", function(data, status){
+            console.log(data)
+            loaded_data = data
+            for (var obj of data) {
+              var sideBAR = document.getElementById('mySidenav')
+              var message = document.createElement('a')
+              var content = document.createTextNode(obj.name)
+              message.id = obj.id
+              message.setAttribute('href', "#")
+              message.setAttribute('onclick', "load_data()")
+              message.appendChild(content)
+              sideBAR.appendChild(message)
+            }
+            
+          });
+    
+    }
+    
+    function closeNav() {
+      document.getElementById("mySidenav").style.width = "0";
+    }
+    function load_data(){
+      var title = document.getElementById("title")
+      tid = event.srcElement.id
+      console.log(tid);
+      $(".title_new").val(loaded_data.find(item => item.id == tid).name)
+      var step;
+      for (step = 1; step <= 10; step++) {
+        $(".txtsrc"+step).val(loaded_data.find(item => item.id == tid).text_content[step-1]) 
+      }
+      var step2;
+      for (step2 = 1; step2 <= 10; step2++) {
+        $(".imgsrc"+step2).val(loaded_data.find(item => item.id == tid).image_urls[step2-1]) 
+      }
+      
+    }
+    
+    </script>
+  <style>
+    #myProgress {
+      width: 100%;
+      background-color: #ddd;
+    }
+    
+    #myBar {
+      width: 10%;
+      height: 30px;
+      background-color: #04AA6D;
+      text-align: center;
+      line-height: 30px;
+      color: white;
+    }
+    </style>
+  <body>
+      
+      <ul id='messages'>
+      </ul>
+      <script>
+          var ws = new WebSocket('ws://www.choozmo.com:8888/progress');
+          ws.onmessage = function(event) {
+              console.log(event.data);
+              var elem = document.getElementById("myBar");
+              elem.style.width = event.data + "%";
+              elem.innerHTML = event.data  + "%";
+          };
+          function sendMessage(event) {
+              var input = document.getElementById("messageText")
+              ws.send(input.value)
+              input.value = ''
+              event.preventDefault()
+          }
+      </script>
+
+
+</div>
+
+</body>
+</html>

+ 191 - 4
main.py

@@ -10,7 +10,6 @@ from typing import List
 import requests
 from pydantic import BaseModel
 from bs4 import BeautifulSoup
-import time
 from PIL import Image,ImageDraw,ImageFont
 import pyttsx3
 import rpyc
@@ -105,6 +104,9 @@ async def index2():
 @app.get("/script_msg.js")
 async def index2():
     return FileResponse('script_msg.js')
+@app.get("/script_msg2.js")
+async def index2():
+    return FileResponse('script_msg2.js')
 @app.get("/style.css")
 async def index2():
     return FileResponse('style.css')
@@ -134,6 +136,25 @@ async def make_anchor_video_v2(req:request2):
     x.start()
     return {"msg":"製作影片需要時間,請您耐心等候  稍後可以在www.choozmo.com:8168/"+req.name+".mp4 中觀看"} 
 
+@app.post("/make_anchor_video_v33")
+async def make_anchor_video_v2(req:request2):
+    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
+    log_table = db['history_input']
+    txt_content_seperate_by_dot = ''
+    for txt in req.text_content:
+        txt_content_seperate_by_dot += txt+","
+    txt_content_seperate_by_dot = txt_content_seperate_by_dot[:-1]
+    img_urls_seperate_by_dot = ''
+    for iurl in req.image_urls:
+        img_urls_seperate_by_dot += iurl+","
+    img_urls_seperate_by_dot = img_urls_seperate_by_dot[:-1]
+    time_stamp = datetime.fromtimestamp(time.time())
+    time_stamp = time_stamp.strftime("%Y-%m-%d %H:%M:%S")
+    pk = log_table.insert({'name':req.name,'text_content':txt_content_seperate_by_dot,'image_urls':img_urls_seperate_by_dot,'timestamp':time_stamp})
+    x = threading.Thread(target=anchor_video_v3333, args=(req.name, req.text_content, req.image_urls))
+    x.start()
+    return {"msg":"製作影片需要時間,請您耐心等候  稍後可以在www.choozmo.com:8168/"+req.name+".mp4 中觀看"} 
+
 @app.websocket("/progress")
 async def websocket_endpoint(websocket: WebSocket):
     await manager.connect(websocket)
@@ -451,9 +472,7 @@ def generate_subtitle_image(name_hash,text_content):
 def anchor_video_v2(name,text_content, image_urls):
     #ws = create_connection("ws://www.choozmo.com:8888/progress")
     progress = 0
-    m = hashlib.md5()
-    m.update(name.encode("utf-8"))
-    name_hash = m.hexdigest()
+    name_hash = str(time.time()).replace('.','')
     
     print('sub image made')
     file_prepare_v2(name, name_hash, text_content,image_urls)
@@ -613,4 +632,172 @@ def anchor_video_v2(name,text_content, image_urls):
     print("video at : www.choozmo.com:8168/"+name_hash+".mp4")
 
     #line notifs
+    
+
+
+
+def anchor_video_v3333(name,text_content, image_urls):
+    ws = create_connection("ws://www.choozmo.com:8888/progress")
+    progress = 0
+
+    name_hash = str(time.time()).replace('.','')
+    
+    print('sub image made')
+    file_prepare_v2(name, name_hash, text_content,image_urls)
+    progress = 10
+    ws.send(str(progress))
+    sub_list=generate_subtitle_image(name_hash,text_content)
+    progress = 20
+    ws.send(str(progress))
+    
+    progress_per_video = int(40/len(text_content))
+    for fname in range(len(text_content)):
+        call_achor_video_v2(name_hash+"/"+str(fname))
+        progress += progress_per_video
+        ws.send(str(progress))
+        print('step finish')
+    print('called............................................')
+
+    ck=cKey(0,254,0,270)
+    ck_anchor=cKey(0,255,1,320)
+    duration = 0
+    #average layer level is 3
+    t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO)
+    t.Open()
+
+    main_timer = 0
+    
+    LOGO_OP = openshot.FFmpegReader(dir_video+"LOGO_OP.mp4")
+    LOGO_OP.Open()         # Open the reader
+    LOGO_OP_clip = video_photo_clip(vid=LOGO_OP,layer=4,position=0,end=LOGO_OP.info.duration
+                    ,location_y=-0.03,scale_x=0.8,scale_y=0.71)
+    t.AddClip(LOGO_OP_clip)
+    bg_head = openshot.FFmpegReader(dir_video+"bg_head.avi")
+    bg_head.Open()
+    bg_head_clip = video_photo_clip(vid=bg_head,layer=2,position=0,end=LOGO_OP.info.duration,ck=ck)
+    t.AddClip(bg_head_clip)
+    main_timer += LOGO_OP.info.duration
+    head_duration = LOGO_OP.info.duration
+    bg_head.Close()
+    LOGO_OP.Close()
+    progress += 10
+    
+
+    
+    clip_duration=0
+    photo_clip_list = [None]*len(text_content)
+    img_list = [None]*len(text_content)
+    anchor_clip_list = [None] * len(text_content)
+    anchor_list = [None] * len(text_content)
+    audio_clip_list = [None] * len(text_content)
+    audio_list = [None] * len(text_content)
+    sub_clip_list = [None] * len(text_content)
+    sub_img_list = [None] * len(text_content)
+    
+    idx = 0
+    for p in listdir(dir_photo+name_hash):
+        
+        anchor_list[idx] = openshot.FFmpegReader(dir_anchor+name_hash+"/"+str(idx)+".mp4")
+        clip_duration = anchor_list[idx].info.duration
+        anchor_list[idx].Open()
+        anchor_clip_list[idx] = video_photo_clip(vid=anchor_list[idx],layer=4,scale_x=0.65,scale_y=0.65,
+                location_x=0.35,location_y=0.25,position=main_timer, end=clip_duration,ck=ck_anchor,audio=False)
+        t.AddClip(anchor_clip_list[idx])
+
+        img_list[idx] = openshot.FFmpegReader(dir_photo+name_hash+'/'+p)
+        img_list[idx].Open()
+        photo_clip_list[idx] = video_photo_clip(vid=img_list[idx],layer=3
+                ,scale_x=0.81,scale_y=0.68,location_y=-0.03,position=main_timer,end=clip_duration,audio=False)
+        t.AddClip(photo_clip_list[idx])
+        img_list[idx].Close()
+
+        audio_list[idx] = openshot.FFmpegReader(dir_sound+name_hash+"/"+str(idx)+".mp3")
+        audio_list[idx].Open()
+        audio_clip_list[idx] = openshot.Clip(audio_list[idx])
+        audio_clip_list[idx].Position(main_timer)
+        audio_clip_list[idx].End(clip_duration)
+        t.AddClip(audio_clip_list[idx])
+
+        img_list[idx].Close()
+        anchor_list[idx].Close()
+        audio_list[idx].Close()
+
+     
+            
+        sub_img_list[idx] = [None] * len(sub_list[idx])
+        sub_clip_list[idx] = [None] * len(sub_list[idx])
+        sub_timer = 0
+        for sub_idx in range(len(sub_list[idx])):
+            sub_img_list[idx][sub_idx] = openshot.QtImageReader(sub_list[idx][sub_idx]['path'])
+            sub_img_list[idx][sub_idx].Open()
+            sub_duration = 0.205*sub_list[idx][sub_idx]['count']
+            sub_clip_list[idx][sub_idx] = video_photo_clip(vid=sub_img_list[idx][sub_idx], layer=6,location_x=0.069, location_y=0.89,position=main_timer+sub_timer,end=sub_duration)
+            t.AddClip(sub_clip_list[idx][sub_idx])
+            sub_img_list[idx][sub_idx].Close()
+            sub_timer += sub_duration
+            print(sub_list[idx][sub_idx]['path'])
+        main_timer += clip_duration
+        idx+=1
+
+    progress+=10
+    ws.send(str(progress))
+    
+    LOGO_ED = openshot.FFmpegReader(dir_video+"LOGO_ED.avi")
+    LOGO_ED.Open()
+    LOGO_ED_clip = video_photo_clip(vid=LOGO_ED,layer=4,position=main_timer,end=LOGO_ED.info.duration+2
+                    ,location_x=0,location_y=-0.02
+                    ,scale_x=0.79,scale_y=0.685)
+    t.AddClip(LOGO_ED_clip)
+    ED_duration = LOGO_ED.info.duration
+    LOGO_ED.Close()
+    
+
+    bg = openshot.FFmpegReader(dir_video+"bg.mp4")
+    bg.Open()
+    bg_times = math.floor(main_timer+ED_duration/bg.info.duration)
+    left_time = (main_timer+ED_duration) % bg.info.duration
+    bg_clip_list = [None] * bg_times
+    bg_list = [None] * bg_times
+    bg.Close()
+    bg_timer = head_duration
+    for idx in range(bg_times):
+        bg_list[idx] = openshot.FFmpegReader(dir_video+"bg.mp4")
+        bg_list[idx].Open()
+        bg_clip_list[idx] = video_photo_clip(bg_list[idx],layer=2,position=bg_timer
+                ,end=bg_list[idx].info.duration,ck=ck)
+        t.AddClip(bg_clip_list[idx])
+        bg_timer += bg_list[idx].info.duration
+        bg_list[idx].Close()
+    bg_left = openshot.FFmpegReader(dir_video+"bg.mp4")
+    bg_left.Open()
+    bg_left_clip = video_photo_clip(bg_left,layer=2,position=bg_timer,end=left_time,ck=ck)
+    t.AddClip(bg_left_clip)
+    bg_left.Close()
+
+    title = openshot.QtImageReader(dir_title+name_hash+".png")
+    title.Open()         # Open the reader
+    title_clip = video_photo_clip(vid=title, layer=4,location_x=-0.047, location_y=0.801,position=0,end=head_duration+main_timer)
+    t.AddClip(title_clip)
+
+    ####start building
+    w = openshot.FFmpegWriter("../html/"+name_hash+".mp4")
+    w.SetAudioOptions(True, "aac", 44100, 2, openshot.LAYOUT_STEREO, 3000000)
+    w.SetVideoOptions(True, "libx264", openshot.Fraction(30000, 1000), 1280, 720,
+        openshot.Fraction(1, 1), False, False, 3000000)
+    w.Open()
+    progress = 100
+    ws.send(str(progress))
+    #may change duration into t.info.duration
+    for n in range(int(t.info.fps)*int(head_duration+main_timer+ED_duration)):
+        f=t.GetFrame(n)
+        w.WriteFrame(f)
+    
+    #notify_group(name+"的影片已經產生完成囉! www.choozmo.com:8168/"+name_hash+".mp4")
+    t.Close()
+    w.Close()
+    
+    print("Video done")
+    print("video at : www.choozmo.com:8168/"+name_hash+".mp4")
+
+    #line notifs
     

+ 1 - 1
script_msg.js

@@ -28,7 +28,7 @@ $(".next").click(function(){
 	obj3 = JSON.stringify(obj2)
   console.log(txtARR);
   console.log(obj3);
-
+  alert('資料已送出! 請耐心等候')
   $.ajax({
 	url: 'http://www.choozmo.com:8888/make_anchor_video_v2',
 	//url: 'http://www.choozmo.com:8888/qqreq',

+ 194 - 0
script_msg2.js

@@ -0,0 +1,194 @@
+//jQuery time
+var current_fs, next_fs, previous_fs; //fieldsets
+var left, opacity, scale; //fieldset properties which we will animate
+var animating; //flag to prevent quick multi-click glitasdaches
+
+$(".next").click(function(){
+  if( !validate() ){
+    return false;
+  }
+  name_title = $('.title_new').val();
+  txtARR=[];
+  imgARR=[];
+  var step;
+	for (step = 1; step <= 10; step++) {
+	  if($(".txtsrc"+step).val()!=""){
+		  txtARR.push($(".txtsrc"+step).val())
+	  }
+	}
+	var step2;
+	for (step2 = 1; step2 <= 10; step2++) {
+	  if($(".imgsrc"+step2).val()!=""){
+		  imgARR.push($(".imgsrc"+step2).val())
+	  }
+	}	
+	dataOBJ = {"name":name_title,"text_content":txtARR,"image_urls":imgARR}
+	objstr = JSON.stringify(dataOBJ);
+	obj2 = JSON.parse(objstr)
+	obj3 = JSON.stringify(obj2)
+  console.log(txtARR);
+  console.log(obj3);
+
+  $.ajax({
+	url: 'http://www.choozmo.com:8888/make_anchor_video_v33',
+	//url: 'http://www.choozmo.com:8888/qqreq',
+	dataType : 'json', // 預期從server接收的資料型態
+  contentType : 'application/json; charset=utf-8', // 要送到server的資料型態
+	type: 'post',
+	data: obj3,
+	success: function(suc_data) {
+        
+	  },
+	//data:JSON.stringify({n1:"12",n2:"22"}),
+	error: function (error) {
+		console.error(error)
+	}
+});
+
+});
+
+$(".previous").click(function(){
+	if(animating) return false;
+	animating = true;
+	
+	current_fs = $(this).parent();
+	previous_fs = $(this).parent().prev();
+	
+	//de-activate current step on progressbar
+	$("#progressbar li").eq($("fieldset").index(current_fs)).removeClass("active");
+	
+	//show the previous fieldset
+	previous_fs.show(); 
+	//hide the current fieldset with style
+	current_fs.animate({opacity: 0}, {
+		step: function(now, mx) {
+			//as the opacity of current_fs reduces to 0 - stored in "now"
+			//1. scale previous_fs from 80% to 100%
+			scale = 0.8 + (1 - now) * 0.2;
+			//2. take current_fs to the right(50%) - from 0%
+			left = ((1-now) * 50)+"%";
+			//3. increase opacity of previous_fs to 1 as it moves in
+			opacity = 1 - now;
+			current_fs.css({'left': left});
+			previous_fs.css({'transform': 'scale('+scale+')', 'opacity': opacity});
+		}, 
+		duration: 800, 
+		complete: function(){
+			current_fs.hide();
+			animating = false;
+		}, 
+		//this comes from the custom easing plugin
+		easing: 'easeInOutBack'
+	});
+});
+
+
+$("input[name=submit]").click(function(){
+	let stop;
+	   console.log($("#msform").serialize());
+	   
+   	if( $('input[name="q9"]:visible').val() !== undefined && !$('input[name="q9"]:visible').prop('checked') ) {
+		stop = 1;
+		$('#term-error').text('必須同意免責聲明與隱私使用政策');
+	}else {
+		stop = 0;
+		$('#term-error').text('');
+	}
+	if(stop == 0) {
+		/* $.ajax({
+			url: '/step_questions/submit',
+			type: 'post',
+			dataType: 'json',
+			data: $("#msform").serialize(),
+			success: function(data) {
+			  showThankyou();
+			},
+			error: function (error) {
+				console.error(error)
+			}
+		}); */
+		showThankyou();
+	}
+});
+
+// 依據管道導頁
+function showThankyou() {
+	document.location = '/a1/index_complete_msg.html';
+	// document.location = '/a1/index_complete_line.html';
+}
+// 跳離頁面
+$('.btn-exit').click(function() {
+	document.location = 'https://hhh.com.tw/';
+})
+
+$('.btn-term-exit').click(function () {
+	self.opener = null;
+	self.close();
+})
+
+function show_input_text(checkbox_id, input_id) {
+	var checkBox = document.getElementById(checkbox_id);
+	var inp = document.getElementById(input_id);
+	if (checkBox.checked == true){
+	  inp.style.display = "block";
+	} else {
+	  inp.style.display = "none";
+	}
+}
+
+function validate() {
+  function q2_validate() { 
+	let phoneno = /^\d{10}$/;
+    if( $('input[name="q2"]:visible').val() !== undefined && $('input[name="q2"]:visible').val().length <= 0) {
+      $('input[name="q2"]:visible').addClass('error');
+      if( !$('.error-text').length )
+          $('input.error').after('<p class="error-text">請輸入正確手機號碼</p>');
+      return false;
+    } else if($('input[name="q2"]:visible').val() !== undefined && !phoneno.test($('input[name="q2"]:visible').val())){
+		$('input[name="q2"]:visible').addClass('error');
+      if( !$('.error-text').length )
+          $('input.error').after('<p class="error-text">請輸入正確手機號碼</p>');
+      return false;
+	}else {
+        $('input[name="q2"]:visible').removeClass('error');
+        $('.error-text').remove();
+        return true;
+    }
+  }
+
+  function q3_validate() {
+	  let emailPattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+    if( $('input[name="q3"]:visible').val() !== undefined && $('input[name="q3"]:visible').val().length <= 0) {
+      $('input[name="q3"]:visible').addClass('error');
+      if( !$('.error-text').length )
+          $('input.error').after('<p class="error-text">請輸入E-mail</p>');
+      return false;
+    } else if ( $('input[name="q3"]:visible').val() !== undefined && !emailPattern.test($('input[name="q3"]:visible').val()) ){
+		$('input[name="q3"]:visible').addClass('error');
+      	if( !$('.error-text').length )
+          $('input.error').after('<p class="error-text">請輸入正確email格式</p>');
+      	return false;
+	}
+    else {
+        $('input[name="q3"]:visible').removeClass('error');
+        $('.error-text').remove();
+        return true;
+    }
+  }
+
+  function q4_validate() {
+	  if( $('input[name="q4"]:visible').val() !== undefined && $('input[name="q4"]:visible').val().length <= 0 ) {
+		$('input[name="q4"]:visible').addClass('error');
+		if( !$('.error-text').length )
+			$('input.error').after('<p class="error-text">請輸入建案名稱</p>');
+		return false;
+	  } 
+	  else {
+		$('input[name="q4"]:visible').removeClass('error');
+        $('.error-text').remove();
+        return true;
+	  }
+  }
+
+  return q2_validate() && q3_validate() && q4_validate();
+}