diff --git a/app/.DS_Store b/app/.DS_Store new file mode 100644 index 0000000..941feef Binary files /dev/null and b/app/.DS_Store differ diff --git a/app/api/.DS_Store b/app/api/.DS_Store new file mode 100644 index 0000000..5389e28 Binary files /dev/null and b/app/api/.DS_Store differ diff --git a/app/api/endpoints/meetings copy.py b/app/api/endpoints/meetings copy.py new file mode 100644 index 0000000..a5e69ce --- /dev/null +++ b/app/api/endpoints/meetings copy.py @@ -0,0 +1,499 @@ + +from fastapi import APIRouter, HTTPException, UploadFile, File, Form +from app.models.models import Meeting, TranscriptSegment, CreateMeetingRequest, UpdateMeetingRequest +from app.core.database import get_db_connection +from app.core.config import UPLOAD_DIR, AUDIO_DIR, MARKDOWN_DIR, ALLOWED_EXTENSIONS, ALLOWED_IMAGE_EXTENSIONS, MAX_FILE_SIZE, MAX_IMAGE_SIZE +from app.services.qiniu_service import qiniu_service +from typing import Optional +import os +import uuid +import shutil + +router = APIRouter() + +@router.get("/meetings", response_model=list[Meeting]) +def get_meetings(user_id: Optional[int] = None): + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + base_query = ''' + SELECT + m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at, + m.user_id as creator_id, u.caption as creator_username + FROM meetings m + JOIN users u ON m.user_id = u.user_id + ''' + + if user_id: + query = f''' + {base_query} + LEFT JOIN attendees a ON m.meeting_id = a.meeting_id + WHERE m.user_id = %s OR a.user_id = %s + GROUP BY m.meeting_id + ORDER BY m.meeting_time DESC, m.created_at DESC + ''' + cursor.execute(query, (user_id, user_id)) + else: + query = f" {base_query} ORDER BY m.meeting_time DESC, m.created_at DESC" + cursor.execute(query) + + meetings = cursor.fetchall() + + meeting_list = [] + for meeting in meetings: + attendees_query = ''' + SELECT u.user_id, u.caption + FROM attendees a + JOIN users u ON a.user_id = u.user_id + WHERE a.meeting_id = %s + ''' + cursor.execute(attendees_query, (meeting['meeting_id'],)) + attendees_data = cursor.fetchall() + attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data] + + meeting_list.append(Meeting( + meeting_id=meeting['meeting_id'], + title=meeting['title'], + meeting_time=meeting['meeting_time'], + summary=meeting['summary'], + created_at=meeting['created_at'], + attendees=attendees, + creator_id=meeting['creator_id'], + creator_username=meeting['creator_username'] + )) + + return meeting_list + +@router.get("/meetings/{meeting_id}", response_model=Meeting) +def get_meeting_details(meeting_id: int): + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + query = ''' + SELECT + m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at, + m.user_id as creator_id, u.caption as creator_username, + af.file_path as audio_file_path + FROM meetings m + JOIN users u ON m.user_id = u.user_id + LEFT JOIN audio_files af ON m.meeting_id = af.meeting_id + WHERE m.meeting_id = %s + ''' + cursor.execute(query, (meeting_id,)) + meeting = cursor.fetchone() + + if not meeting: + raise HTTPException(status_code=404, detail="Meeting not found") + + attendees_query = ''' + SELECT u.user_id, u.caption + FROM attendees a + JOIN users u ON a.user_id = u.user_id + WHERE a.meeting_id = %s + ''' + cursor.execute(attendees_query, (meeting['meeting_id'],)) + attendees_data = cursor.fetchall() + attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data] + + meeting_data = Meeting( + meeting_id=meeting['meeting_id'], + title=meeting['title'], + meeting_time=meeting['meeting_time'], + summary=meeting['summary'], + created_at=meeting['created_at'], + attendees=attendees, + creator_id=meeting['creator_id'], + creator_username=meeting['creator_username'] + ) + + # Add audio file path if exists + if meeting['audio_file_path']: + meeting_data.audio_file_path = meeting['audio_file_path'] + + return meeting_data + +@router.get("/meetings/{meeting_id}/transcript", response_model=list[TranscriptSegment]) +def get_meeting_transcript(meeting_id: int): + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + # First check if meeting exists + meeting_query = "SELECT meeting_id FROM meetings WHERE meeting_id = %s" + cursor.execute(meeting_query, (meeting_id,)) + if not cursor.fetchone(): + raise HTTPException(status_code=404, detail="Meeting not found") + + # Get transcript segments + transcript_query = ''' + SELECT segment_id, meeting_id, speaker_tag, start_time_ms, end_time_ms, text_content + FROM transcript_segments + WHERE meeting_id = %s + ORDER BY start_time_ms ASC + ''' + cursor.execute(transcript_query, (meeting_id,)) + segments = cursor.fetchall() + + return [TranscriptSegment(**segment) for segment in segments] + +@router.post("/meetings") +def create_meeting(meeting_request: CreateMeetingRequest): + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + # Create meeting + meeting_query = ''' + INSERT INTO meetings (user_id, title, meeting_time, summary) + VALUES (%s, %s, %s, %s) + ''' + # Note: You'll need to pass user_id, for now using hardcoded value + cursor.execute(meeting_query, (1, meeting_request.title, meeting_request.meeting_time, None)) + meeting_id = cursor.lastrowid + + # Add attendees + for attendee_id in meeting_request.attendee_ids: + attendee_query = ''' + INSERT INTO attendees (meeting_id, user_id) + VALUES (%s, %s) + ON DUPLICATE KEY UPDATE meeting_id = meeting_id + ''' + cursor.execute(attendee_query, (meeting_id, attendee_id)) + + connection.commit() + return {"meeting_id": meeting_id, "message": "Meeting created successfully"} + +@router.put("/meetings/{meeting_id}") +def update_meeting(meeting_id: int, meeting_request: UpdateMeetingRequest): + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + # Check if meeting exists + cursor.execute("SELECT meeting_id FROM meetings WHERE meeting_id = %s", (meeting_id,)) + if not cursor.fetchone(): + raise HTTPException(status_code=404, detail="Meeting not found") + + # Update meeting + update_query = ''' + UPDATE meetings + SET title = %s, meeting_time = %s, summary = %s + WHERE meeting_id = %s + ''' + cursor.execute(update_query, ( + meeting_request.title, + meeting_request.meeting_time, + meeting_request.summary, + meeting_id + )) + + # Update attendees - remove existing ones and add new ones + cursor.execute("DELETE FROM attendees WHERE meeting_id = %s", (meeting_id,)) + + for attendee_id in meeting_request.attendee_ids: + attendee_query = ''' + INSERT INTO attendees (meeting_id, user_id) + VALUES (%s, %s) + ''' + cursor.execute(attendee_query, (meeting_id, attendee_id)) + + connection.commit() + return {"message": "Meeting updated successfully"} + +@router.delete("/meetings/{meeting_id}") +def delete_meeting(meeting_id: int): + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + # Check if meeting exists + cursor.execute("SELECT meeting_id FROM meetings WHERE meeting_id = %s", (meeting_id,)) + if not cursor.fetchone(): + raise HTTPException(status_code=404, detail="Meeting not found") + + # Delete related records first (foreign key constraints) + cursor.execute("DELETE FROM transcript_segments WHERE meeting_id = %s", (meeting_id,)) + cursor.execute("DELETE FROM audio_files WHERE meeting_id = %s", (meeting_id,)) + cursor.execute("DELETE FROM attachments WHERE meeting_id = %s", (meeting_id,)) + cursor.execute("DELETE FROM attendees WHERE meeting_id = %s", (meeting_id,)) + + # Delete meeting + cursor.execute("DELETE FROM meetings WHERE meeting_id = %s", (meeting_id,)) + + connection.commit() + return {"message": "Meeting deleted successfully"} + +@router.post("/meetings/{meeting_id}/regenerate-summary") +def regenerate_summary(meeting_id: int): + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + # Check if meeting exists + cursor.execute("SELECT meeting_id FROM meetings WHERE meeting_id = %s", (meeting_id,)) + if not cursor.fetchone(): + raise HTTPException(status_code=404, detail="Meeting not found") + + # For now, return a mock summary + # In a real implementation, this would call an AI service + mock_summary = """# AI 生成摘要 + +## 主要议题 +- 项目进度回顾 +- 技术方案讨论 +- 下阶段规划 + +## 关键决策 +- 采用新的技术架构 +- 调整项目时间节点 +- 分配任务责任 + +## 后续行动 +- [ ] 完成技术方案文档 +- [ ] 安排下次会议时间 +- [ ] 跟进项目进度""" + + # Update meeting summary + cursor.execute( + "UPDATE meetings SET summary = %s WHERE meeting_id = %s", + (mock_summary, meeting_id) + ) + connection.commit() + + return {"summary": mock_summary} + +@router.get("/meetings/{meeting_id}/edit", response_model=Meeting) +def get_meeting_for_edit(meeting_id: int): + """Get meeting details with full attendee information for editing""" + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + query = ''' + SELECT + m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at, + m.user_id as creator_id, u.caption as creator_username, + af.file_path as audio_file_path + FROM meetings m + JOIN users u ON m.user_id = u.user_id + LEFT JOIN audio_files af ON m.meeting_id = af.meeting_id + WHERE m.meeting_id = %s + ''' + cursor.execute(query, (meeting_id,)) + meeting = cursor.fetchone() + + if not meeting: + raise HTTPException(status_code=404, detail="Meeting not found") + + # Get attendees with full info for editing + attendees_query = ''' + SELECT u.user_id, u.caption + FROM attendees a + JOIN users u ON a.user_id = u.user_id + WHERE a.meeting_id = %s + ''' + cursor.execute(attendees_query, (meeting['meeting_id'],)) + attendees_data = cursor.fetchall() + attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data] + + meeting_data = Meeting( + meeting_id=meeting['meeting_id'], + title=meeting['title'], + meeting_time=meeting['meeting_time'], + summary=meeting['summary'], + created_at=meeting['created_at'], + attendees=attendees, + creator_id=meeting['creator_id'], + creator_username=meeting['creator_username'] + ) + + # Add audio file path if exists + if meeting['audio_file_path']: + meeting_data.audio_file_path = meeting['audio_file_path'] + + return meeting_data + +@router.post("/meetings/upload-audio") +async def upload_audio( + audio_file: UploadFile = File(...), + meeting_id: int = Form(...) +): + # Validate file extension + file_extension = os.path.splitext(audio_file.filename)[1].lower() + if file_extension not in ALLOWED_EXTENSIONS: + raise HTTPException( + status_code=400, + detail=f"Unsupported file type. Allowed types: {', '.join(ALLOWED_EXTENSIONS)}" + ) + + # Check file size + if audio_file.size > MAX_FILE_SIZE: + raise HTTPException( + status_code=400, + detail="File size exceeds 100MB limit" + ) + + # Check if meeting exists + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + cursor.execute("SELECT meeting_id FROM meetings WHERE meeting_id = %s", (meeting_id,)) + if not cursor.fetchone(): + raise HTTPException(status_code=404, detail="Meeting not found") + + # TEMP: Use existing file to test Qiniu upload instead of client file + # This bypasses potential client file processing issues + existing_file = AUDIO_DIR / "31ce039a-f619-4869-91c8-eab934bbd1d4.m4a" + if not existing_file.exists(): + raise HTTPException(status_code=500, detail="Test file not found") + + temp_path = existing_file + print(f"DEBUG: Using existing test file: {temp_path}") + print(f"DEBUG: Test file exists: {temp_path.exists()}") + print(f"DEBUG: Test file size: {temp_path.stat().st_size}") + + # Upload to Qiniu + try: + print(f"DEBUG: Attempting to upload audio to Qiniu - meeting_id: {meeting_id}, filename: {audio_file.filename}") + print(f"DEBUG: Temp file path: {temp_path}") + print(f"DEBUG: Temp file exists: {temp_path.exists()}") + + success, qiniu_url, error_msg = qiniu_service.upload_audio_file( + str(temp_path), meeting_id, audio_file.filename + ) + + print(f"DEBUG: Qiniu upload result - success: {success}, url: {qiniu_url}, error: {error_msg}") + + # TEMP: Don't delete existing test file + # if temp_path.exists(): + # temp_path.unlink() + + if not success: + raise HTTPException(status_code=500, detail=f"Failed to upload to Qiniu: {error_msg}") + + # Save file info to database with Qiniu URL + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + # Insert audio file record with Qiniu URL + insert_query = ''' + INSERT INTO audio_files (meeting_id, file_name, file_path, file_size, upload_time) + VALUES (%s, %s, %s, %s, NOW()) + ON DUPLICATE KEY UPDATE + file_name = VALUES(file_name), + file_path = VALUES(file_path), + file_size = VALUES(file_size), + upload_time = VALUES(upload_time) + ''' + cursor.execute(insert_query, (meeting_id, audio_file.filename, qiniu_url, audio_file.size)) + connection.commit() + + return { + "message": "Audio file uploaded successfully to Qiniu", + "file_name": audio_file.filename, + "file_path": qiniu_url, + "qiniu_url": qiniu_url + } + + except Exception as e: + print(f"DEBUG: Exception in audio upload: {str(e)}") + print(f"DEBUG: Exception type: {type(e)}") + import traceback + print(f"DEBUG: Traceback: {traceback.format_exc()}") + # TEMP: Don't delete existing test file in case of error + # if temp_path.exists(): + # temp_path.unlink() + raise HTTPException(status_code=500, detail=f"Upload failed: {str(e)}") + +@router.get("/meetings/{meeting_id}/audio") +def get_audio_file(meeting_id: int): + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + query = ''' + SELECT file_name, file_path, file_size, upload_time + FROM audio_files + WHERE meeting_id = %s + ''' + cursor.execute(query, (meeting_id,)) + audio_file = cursor.fetchone() + + if not audio_file: + raise HTTPException(status_code=404, detail="Audio file not found for this meeting") + + return { + "file_name": audio_file['file_name'], + "file_path": audio_file['file_path'], + "file_size": audio_file['file_size'], + "upload_time": audio_file['upload_time'] + } + +@router.post("/meetings/{meeting_id}/upload-image") +async def upload_image( + meeting_id: int, + image_file: UploadFile = File(...) +): + # Validate file extension + file_extension = os.path.splitext(image_file.filename)[1].lower() + if file_extension not in ALLOWED_IMAGE_EXTENSIONS: + raise HTTPException( + status_code=400, + detail=f"Unsupported image type. Allowed types: {', '.join(ALLOWED_IMAGE_EXTENSIONS)}" + ) + + # Check file size + if image_file.size > MAX_IMAGE_SIZE: + raise HTTPException( + status_code=400, + detail="Image size exceeds 10MB limit" + ) + + # Check if meeting exists + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + cursor.execute("SELECT meeting_id FROM meetings WHERE meeting_id = %s", (meeting_id,)) + if not cursor.fetchone(): + raise HTTPException(status_code=404, detail="Meeting not found") + + # Create temporary file for upload + temp_filename = f"{uuid.uuid4()}{file_extension}" + temp_path = MARKDOWN_DIR / temp_filename + + # Save file temporarily + # Save file temporarily + try: + contents = await image_file.read() + with open(temp_path, "wb") as buffer: + buffer.write(contents) + except Exception as e: + raise HTTPException(status_code=500, detail=f"Failed to save temporary image: {str(e)}") + + # Upload to Qiniu + try: + print(f"DEBUG: Attempting to upload image to Qiniu - meeting_id: {meeting_id}, filename: {image_file.filename}") + print(f"DEBUG: Temp file path: {temp_path}") + print(f"DEBUG: Temp file exists: {temp_path.exists()}") + + success, qiniu_url, error_msg = qiniu_service.upload_markdown_image( + str(temp_path), meeting_id, image_file.filename + ) + + print(f"DEBUG: Qiniu upload result - success: {success}, url: {qiniu_url}, error: {error_msg}") + + # Clean up temporary file + if temp_path.exists(): + temp_path.unlink() + + if not success: + raise HTTPException(status_code=500, detail=f"Failed to upload image to Qiniu: {error_msg}") + + return { + "message": "Image uploaded successfully to Qiniu", + "file_name": image_file.filename, + "file_path": qiniu_url, + "url": qiniu_url, + "qiniu_url": qiniu_url + } + + except Exception as e: + print(f"DEBUG: Exception in image upload: {str(e)}") + print(f"DEBUG: Exception type: {type(e)}") + import traceback + print(f"DEBUG: Traceback: {traceback.format_exc()}") + # Clean up temporary file in case of error + if temp_path.exists(): + temp_path.unlink() + raise HTTPException(status_code=500, detail=f"Image upload failed: {str(e)}") diff --git a/app/api/endpoints/meetings.py b/app/api/endpoints/meetings.py index 7ce8cf4..a639736 100644 --- a/app/api/endpoints/meetings.py +++ b/app/api/endpoints/meetings.py @@ -2,7 +2,8 @@ from fastapi import APIRouter, HTTPException, UploadFile, File, Form from app.models.models import Meeting, TranscriptSegment, CreateMeetingRequest, UpdateMeetingRequest from app.core.database import get_db_connection -from app.core.config import UPLOAD_DIR, AUDIO_DIR, MARKDOWN_DIR, ALLOWED_EXTENSIONS, ALLOWED_IMAGE_EXTENSIONS, MAX_FILE_SIZE, MAX_IMAGE_SIZE +from app.core.config import BASE_DIR, UPLOAD_DIR, AUDIO_DIR, MARKDOWN_DIR, ALLOWED_EXTENSIONS, ALLOWED_IMAGE_EXTENSIONS, MAX_FILE_SIZE, MAX_IMAGE_SIZE +from app.services.qiniu_service import qiniu_service from typing import Optional import os import uuid @@ -144,8 +145,7 @@ def create_meeting(meeting_request: CreateMeetingRequest): INSERT INTO meetings (user_id, title, meeting_time, summary) VALUES (%s, %s, %s, %s) ''' - # Note: You'll need to pass user_id, for now using hardcoded value - cursor.execute(meeting_query, (1, meeting_request.title, meeting_request.meeting_time, None)) + cursor.execute(meeting_query, (meeting_request.user_id, meeting_request.title, meeting_request.meeting_time, None)) meeting_id = cursor.lastrowid # Add attendees @@ -326,16 +326,18 @@ async def upload_audio( detail="File size exceeds 100MB limit" ) + # Create meeting-specific directory + meeting_dir = AUDIO_DIR / str(meeting_id) + meeting_dir.mkdir(exist_ok=True) + # Generate unique filename unique_filename = f"{uuid.uuid4()}{file_extension}" - file_path = AUDIO_DIR / unique_filename - - # Store only relative path for database (audio/filename) - relative_path = f"audio/{unique_filename}" + absolute_path = meeting_dir / unique_filename + relative_path = absolute_path.relative_to(BASE_DIR) # Save file try: - with open(file_path, "wb") as buffer: + with open(absolute_path, "wb") as buffer: shutil.copyfileobj(audio_file.file, buffer) except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to save file: {str(e)}") @@ -348,7 +350,7 @@ async def upload_audio( cursor.execute("SELECT meeting_id FROM meetings WHERE meeting_id = %s", (meeting_id,)) if not cursor.fetchone(): # Clean up uploaded file if meeting doesn't exist - os.remove(file_path) + os.remove(absolute_path) raise HTTPException(status_code=404, detail="Meeting not found") # Insert audio file record @@ -361,13 +363,13 @@ async def upload_audio( file_size = VALUES(file_size), upload_time = VALUES(upload_time) ''' - cursor.execute(insert_query, (meeting_id, audio_file.filename, relative_path, audio_file.size)) + cursor.execute(insert_query, (meeting_id, audio_file.filename, '/'+str(relative_path), audio_file.size)) connection.commit() return { "message": "Audio file uploaded successfully", "file_name": audio_file.filename, - "file_path": relative_path + "file_path": '/'+str(relative_path) } @router.get("/meetings/{meeting_id}/audio") @@ -426,14 +428,12 @@ async def upload_image( # Generate unique filename unique_filename = f"{uuid.uuid4()}{file_extension}" - file_path = meeting_dir / unique_filename - - # Store relative path for URL access - relative_path = f"markdown/{meeting_id}/{unique_filename}" + absolute_path = meeting_dir / unique_filename + relative_path = absolute_path.relative_to(BASE_DIR) # Save file try: - with open(file_path, "wb") as buffer: + with open(absolute_path, "wb") as buffer: shutil.copyfileobj(image_file.file, buffer) except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to save image: {str(e)}") @@ -441,6 +441,5 @@ async def upload_image( return { "message": "Image uploaded successfully", "file_name": image_file.filename, - "file_path": relative_path, - "url": f"/uploads/{relative_path}" + "file_path": '/'+ str(relative_path) } diff --git a/app/core/config.py b/app/core/config.py index 84de2c2..1c07945 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -33,4 +33,13 @@ API_CONFIG = { 'host': os.getenv('API_HOST', '0.0.0.0'), 'port': int(os.getenv('API_PORT', '8000')), 'cors_origins': os.getenv('CORS_ORIGINS', 'http://localhost:5173').split(',') -} \ No newline at end of file +} + +# 七牛云配置 +QINIU_ACCESS_KEY = os.getenv('QINIU_ACCESS_KEY', 'A0tp96HCtg-wZCughTgi5vc2pJnw3btClwxRE_e8') +QINIU_SECRET_KEY = os.getenv('QINIU_SECRET_KEY', 'Lj-MSHpaVbmzpS86kMIjmwikvYOT9iPBjCk9hm6k') +QINIU_BUCKET = os.getenv('QINIU_BUCKET', 'imeeting') +QINIU_DOMAIN = os.getenv('QINIU_DOMAIN', 't0vogyxkz.hn-bkt.clouddn.com') + +# Dashscope (Tongyi Qwen) API Key +DASHSCOPE_API_KEY = os.getenv('DASHSCOPE_API_KEY', 'sk-c2bf06ea56b4491ea3d1e37fdb472b8f') \ No newline at end of file diff --git a/app/models/models.py b/app/models/models.py index 3c387aa..44100e2 100644 --- a/app/models/models.py +++ b/app/models/models.py @@ -47,6 +47,7 @@ class TranscriptSegment(BaseModel): text_content: str class CreateMeetingRequest(BaseModel): + user_id: int title: str meeting_time: Optional[datetime.datetime] attendee_ids: list[int] diff --git a/app/services/ai_service.py b/app/services/ai_service.py new file mode 100644 index 0000000..d2b128b --- /dev/null +++ b/app/services/ai_service.py @@ -0,0 +1,106 @@ +from http import HTTPStatus +import requests +import json +import dashscope +from dashscope.audio.asr import Transcription +from app.core.config import DASHSCOPE_API_KEY +from app.core.database import get_db_connection + +class AIService: + def __init__(self): + dashscope.api_key = DASHSCOPE_API_KEY + + def transcribe(self, file_urls: list[str], meeting_id: int): + print(f"Starting transcription for meeting_id: {meeting_id}, files: {file_urls}") + + try: + task_response = Transcription.async_call( + model='paraformer-v2', + file_urls=file_urls, + language_hints=['zh', 'en'], + disfluency_removal_enabled=True, + diarization_enabled=True, + speaker_count=10 + ) + + transcribe_response = Transcription.wait(task=task_response.output.task_id) + + if transcribe_response.status_code != HTTPStatus.OK: + print(f"Transcription failed: {transcribe_response.status_code}, {transcribe_response.message}") + return + + print("Transcription task submitted successfully!") + if not (transcribe_response.output and transcribe_response.output.get('results')): + print("No transcription results found in the response.") + return + + transcription_url = transcribe_response.output['results'][0]['transcription_url'] + print(f"Fetching transcription from URL: {transcription_url}") + + response = requests.get(transcription_url) + response.raise_for_status() + transcription_data = response.json() + + self._save_segments_to_db(transcription_data, meeting_id) + + except requests.exceptions.RequestException as e: + print(f"Error fetching transcription from URL: {e}") + except json.JSONDecodeError as e: + print(f"Error decoding JSON from transcription URL: {e}") + except Exception as e: + print(f"An unexpected error occurred: {e}") + + def _save_segments_to_db(self, data: dict, meeting_id: int): + segments_to_insert = [] + for transcript in data.get('transcripts', []): + for sentence in transcript.get('sentences', []): + segments_to_insert.append(( + meeting_id, + sentence.get('speaker_id', 'Unknown'), + sentence.get('begin_time'), + sentence.get('end_time'), + sentence.get('text') + )) + + if not segments_to_insert: + print("No segments to save.") + return + + try: + with get_db_connection() as connection: + cursor = connection.cursor() + + # Clear existing segments for this meeting to avoid duplicates + delete_query = "DELETE FROM transcript_segments WHERE meeting_id = %s" + cursor.execute(delete_query, (meeting_id,)) + print(f"Deleted existing segments for meeting_id: {meeting_id}") + + insert_query = ''' + INSERT INTO transcript_segments (meeting_id, speaker_tag, start_time_ms, end_time_ms, text_content) + VALUES (%s, %s, %s, %s, %s) + ''' + cursor.executemany(insert_query, segments_to_insert) + connection.commit() + print(f"Successfully saved {len(segments_to_insert)} segments to the database for meeting_id: {meeting_id}") + + except Exception as e: + print(f"Database error: {e}") + +# Main method for testing +if __name__ == '__main__': + # This is an example of how to use the service. + # You need to provide a valid meeting_id that exists in your database + # and a publicly accessible URL for the audio file. + + # Example usage: + # 1. Make sure you have a meeting with meeting_id = 1 in your database. + # 2. Make sure the audio file URL is correct and accessible. + + test_meeting_id = 37 + # Please replace with your own publicly accessible audio file URL + test_file_urls = ['http://t0vogyxkz.hn-bkt.clouddn.com/record/meeting_records_2.mp3'] + + print("--- Running AI Service Test ---") + ai_service = AIService() + ai_service.transcribe(file_urls=test_file_urls, meeting_id=test_meeting_id) + print("--- AI Service Test Finished ---") \ No newline at end of file diff --git a/app/services/qiniu_service.py b/app/services/qiniu_service.py new file mode 100644 index 0000000..427dcd9 --- /dev/null +++ b/app/services/qiniu_service.py @@ -0,0 +1,147 @@ +from qiniu import Auth, put_file_v2, etag, BucketManager +import qiniu.config +import os +import uuid +from typing import Optional, Tuple +from app.core.config import QINIU_ACCESS_KEY, QINIU_SECRET_KEY, QINIU_BUCKET, QINIU_DOMAIN + + +class QiniuService: + def __init__(self): + self.access_key = QINIU_ACCESS_KEY + self.secret_key = QINIU_SECRET_KEY + self.bucket_name = QINIU_BUCKET + self.domain = QINIU_DOMAIN + + if not self.access_key or not self.secret_key: + print("ERROR: 七牛云Access Key或Secret Key为空!") + if not self.bucket_name: + print("ERROR: 七牛云Bucket名称为空!") + if not self.domain: + print("ERROR: 七牛云Domain为空!") + + self.q = Auth(self.access_key, self.secret_key) + + + def upload_audio_file(self, file_path: str, meeting_id: int, original_filename: str) -> Tuple[bool, str, Optional[str]]: + """ + Upload audio file to Qiniu cloud storage + + Args: + file_path: Local file path + meeting_id: Meeting ID for directory structure + original_filename: Original filename + + Returns: + Tuple of (success: bool, url: str, error_message: Optional[str]) + """ + try: + # Validate file exists + if not os.path.exists(file_path): + return False, "", f"File not found: {file_path}" + + file_extension = os.path.splitext(original_filename)[1] + unique_filename = f"{uuid.uuid4()}{file_extension}" + key = f"record/{meeting_id}/{unique_filename}" + + # Generate upload token + token = self.q.upload_token(self.bucket_name, key, 3600) + print(f"DEBUG: 生成音频上传token成功, key: {key}") + print(f"DEBUG: token前50位: {token[:50] if token else 'None'}") + + # Upload file with retry mechanism + ret, info = put_file_v2(token, key, file_path) + + print(f"DEBUG: Qiniu upload response - ret: {ret}, info: {info}") + print(f"DEBUG: Qiniu upload info details - status_code: {info.status_code}, text_body: {info.text_body}") + + # Check if upload was successful + # For put_file_v2, we need to check the info.status_code + if info.status_code == 200 and ret and 'key' in ret and ret['key'] == key: + url = f"http://{self.domain}/{key}" + return True, url, None + else: + # Extract error information properly + error_msg = f"Upload failed: status_code={info.status_code}" + if hasattr(info, 'text_body') and info.text_body: + error_msg += f", response={info.text_body}" + return False, "", error_msg + + except Exception as e: + import traceback + traceback.print_exc() + return False, "", f"Upload error: {str(e)}" + + def upload_markdown_image(self, file_path: str, meeting_id: int, original_filename: str) -> Tuple[bool, str, Optional[str]]: + """ + Upload markdown image to Qiniu cloud storage + + Args: + file_path: Local file path + meeting_id: Meeting ID for directory structure + original_filename: Original filename + + Returns: + Tuple of (success: bool, url: str, error_message: Optional[str]) + """ + try: + # Validate file exists + if not os.path.exists(file_path): + return False, "", f"File not found: {file_path}" + + file_extension = os.path.splitext(original_filename)[1] + unique_filename = f"{uuid.uuid4()}{file_extension}" + key = f"markdown/{meeting_id}/{unique_filename}" + + # Generate upload token + token = self.q.upload_token(self.bucket_name, key, 3600) + + # Upload file with retry mechanism + ret, info = put_file_v2(token, key, file_path) + + print(f"DEBUG: Qiniu image upload response - ret: {ret}, info: {info}") + print(f"DEBUG: Qiniu image upload info details - status_code: {info.status_code}, text_body: {info.text_body}") + + # Check if upload was successful + # For put_file_v2, we need to check the info.status_code + if info.status_code == 200 and ret and 'key' in ret and ret['key'] == key: + url = f"http://{self.domain}/{key}" + return True, url, None + else: + # Extract error information properly + error_msg = f"Upload failed: status_code={info.status_code}" + if hasattr(info, 'text_body') and info.text_body: + error_msg += f", response={info.text_body}" + return False, "", error_msg + + except Exception as e: + import traceback + traceback.print_exc() + return False, "", f"Upload error: {str(e)}" + + def delete_file(self, key: str) -> Tuple[bool, Optional[str]]: + """ + Delete file from Qiniu cloud storage + + Args: + key: File key in Qiniu storage + + Returns: + Tuple of (success: bool, error_message: Optional[str]) + """ + try: + from qiniu import BucketManager + bucket = BucketManager(self.q) + ret, info = bucket.delete(self.bucket_name, key) + + if ret is None: + return True, None + else: + return False, f"Delete failed: {info}" + + except Exception as e: + return False, f"Delete error: {str(e)}" + + +# Global instance +qiniu_service = QiniuService() \ No newline at end of file diff --git a/app/services/result.json b/app/services/result.json new file mode 100644 index 0000000..0788712 --- /dev/null +++ b/app/services/result.json @@ -0,0 +1,1707 @@ +{ + "file_url": "http://t0vogyxkz.hn-bkt.clouddn.com/records/meeting_records_1.mp3", + "properties": { + "audio_format": "mp3", + "channels": [ + 0, + 1 + ], + "original_sampling_rate": 44100, + "original_duration_in_milliseconds": 60000 + }, + "transcripts": [ + { + "channel_id": 0, + "content_duration_in_milliseconds": 57300, + "text": "卖了工时是吧?因为这项目消耗了哪些人的工时?嗯,因为你这个项目一出来,这项目比如说100个工时,对不对?嗯,那自然就是这项目的100工时消耗在哪人身上,是吧?这是第一个。第二,这一个人在时间段里是吧,这一个人在一个月之内他消他的工时消耗在哪些项目上。对,第三个是这十个人在这个一个月就总的这个这大坨工时消耗在哪些时间段里?嗯,呃现在可能就是你说的那第三个还没有找到一个比较好的表达方式。呃,第二个第二个你看第二个就是这个就是我们选择一个人啊,比如说我们选曹冲也可以哈,然后呢这个就是在在这个时间段里,对吧?比如说他一个月或或者一个月嘛,或者一个月到31号、29号,你看这就可以看。出长春这。两天没,不好意思。", + "sentences": [ + { + "begin_time": 0, + "end_time": 1152, + "text": "卖了工时是吧?", + "sentence_id": 1, + "speaker_id": 0, + "words": [ + { + "begin_time": 0, + "end_time": 192, + "text": "卖", + "punctuation": "" + }, + { + "begin_time": 192, + "end_time": 384, + "text": "了", + "punctuation": "" + }, + { + "begin_time": 384, + "end_time": 576, + "text": "工", + "punctuation": "" + }, + { + "begin_time": 576, + "end_time": 768, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 768, + "end_time": 960, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 960, + "end_time": 1152, + "text": "吧", + "punctuation": "?" + } + ] + }, + { + "begin_time": 1152, + "end_time": 3840, + "text": "因为这项目消耗了哪些人的工时?", + "sentence_id": 2, + "speaker_id": 0, + "words": [ + { + "begin_time": 1152, + "end_time": 1344, + "text": "因", + "punctuation": "" + }, + { + "begin_time": 1344, + "end_time": 1536, + "text": "为", + "punctuation": "" + }, + { + "begin_time": 1536, + "end_time": 1728, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 1728, + "end_time": 1920, + "text": "项", + "punctuation": "" + }, + { + "begin_time": 1920, + "end_time": 2112, + "text": "目", + "punctuation": "" + }, + { + "begin_time": 2112, + "end_time": 2304, + "text": "消", + "punctuation": "" + }, + { + "begin_time": 2304, + "end_time": 2496, + "text": "耗", + "punctuation": "" + }, + { + "begin_time": 2496, + "end_time": 2688, + "text": "了", + "punctuation": "" + }, + { + "begin_time": 2688, + "end_time": 2880, + "text": "哪", + "punctuation": "" + }, + { + "begin_time": 2880, + "end_time": 3072, + "text": "些", + "punctuation": "" + }, + { + "begin_time": 3072, + "end_time": 3264, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 3264, + "end_time": 3456, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 3456, + "end_time": 3648, + "text": "工", + "punctuation": "" + }, + { + "begin_time": 3648, + "end_time": 3840, + "text": "时", + "punctuation": "?" + } + ] + }, + { + "begin_time": 3840, + "end_time": 8640, + "text": "嗯,因为你这个项目一出来,这项目比如说100个工时,对不对?", + "sentence_id": 3, + "speaker_id": 0, + "words": [ + { + "begin_time": 3840, + "end_time": 4032, + "text": "嗯", + "punctuation": "," + }, + { + "begin_time": 4032, + "end_time": 4224, + "text": "因", + "punctuation": "" + }, + { + "begin_time": 4224, + "end_time": 4416, + "text": "为", + "punctuation": "" + }, + { + "begin_time": 4416, + "end_time": 4608, + "text": "你", + "punctuation": "" + }, + { + "begin_time": 4608, + "end_time": 4800, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 4800, + "end_time": 4992, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 4992, + "end_time": 5184, + "text": "项", + "punctuation": "" + }, + { + "begin_time": 5184, + "end_time": 5376, + "text": "目", + "punctuation": "" + }, + { + "begin_time": 5376, + "end_time": 5568, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 5568, + "end_time": 5760, + "text": "出", + "punctuation": "" + }, + { + "begin_time": 5760, + "end_time": 5952, + "text": "来", + "punctuation": "," + }, + { + "begin_time": 5952, + "end_time": 6144, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 6144, + "end_time": 6336, + "text": "项", + "punctuation": "" + }, + { + "begin_time": 6336, + "end_time": 6528, + "text": "目", + "punctuation": "" + }, + { + "begin_time": 6528, + "end_time": 6720, + "text": "比", + "punctuation": "" + }, + { + "begin_time": 6720, + "end_time": 6912, + "text": "如", + "punctuation": "" + }, + { + "begin_time": 6912, + "end_time": 7104, + "text": "说", + "punctuation": "" + }, + { + "begin_time": 7104, + "end_time": 7872, + "text": "100个工", + "punctuation": "" + }, + { + "begin_time": 7872, + "end_time": 8064, + "text": "时", + "punctuation": "," + }, + { + "begin_time": 8064, + "end_time": 8256, + "text": "对", + "punctuation": "" + }, + { + "begin_time": 8256, + "end_time": 8448, + "text": "不", + "punctuation": "" + }, + { + "begin_time": 8448, + "end_time": 8640, + "text": "对", + "punctuation": "?" + } + ] + }, + { + "begin_time": 8640, + "end_time": 13056, + "text": "嗯,那自然就是这项目的100工时消耗在哪人身上,是吧?", + "sentence_id": 4, + "speaker_id": 0, + "words": [ + { + "begin_time": 8640, + "end_time": 8832, + "text": "嗯", + "punctuation": "," + }, + { + "begin_time": 8832, + "end_time": 9024, + "text": "那", + "punctuation": "" + }, + { + "begin_time": 9024, + "end_time": 9216, + "text": "自", + "punctuation": "" + }, + { + "begin_time": 9216, + "end_time": 9408, + "text": "然", + "punctuation": "" + }, + { + "begin_time": 9408, + "end_time": 9600, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 9600, + "end_time": 9792, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 9792, + "end_time": 9984, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 9984, + "end_time": 10176, + "text": "项", + "punctuation": "" + }, + { + "begin_time": 10176, + "end_time": 10368, + "text": "目", + "punctuation": "" + }, + { + "begin_time": 10368, + "end_time": 10560, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 10560, + "end_time": 11328, + "text": "100工时", + "punctuation": "" + }, + { + "begin_time": 11328, + "end_time": 11520, + "text": "消", + "punctuation": "" + }, + { + "begin_time": 11520, + "end_time": 11712, + "text": "耗", + "punctuation": "" + }, + { + "begin_time": 11712, + "end_time": 11904, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 11904, + "end_time": 12096, + "text": "哪", + "punctuation": "" + }, + { + "begin_time": 12096, + "end_time": 12288, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 12288, + "end_time": 12480, + "text": "身", + "punctuation": "" + }, + { + "begin_time": 12480, + "end_time": 12672, + "text": "上", + "punctuation": "," + }, + { + "begin_time": 12672, + "end_time": 12864, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 12864, + "end_time": 13056, + "text": "吧", + "punctuation": "?" + } + ] + }, + { + "begin_time": 13056, + "end_time": 14016, + "text": "这是第一个。", + "sentence_id": 5, + "speaker_id": 0, + "words": [ + { + "begin_time": 13056, + "end_time": 13248, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 13248, + "end_time": 13440, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 13440, + "end_time": 13632, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 13632, + "end_time": 13824, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 13824, + "end_time": 14016, + "text": "个", + "punctuation": "。" + } + ] + }, + { + "begin_time": 14016, + "end_time": 21120, + "text": "第二,这一个人在时间段里是吧,这一个人在一个月之内他消他的工时消耗在哪些项目上。", + "sentence_id": 6, + "speaker_id": 0, + "words": [ + { + "begin_time": 14016, + "end_time": 14208, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 14208, + "end_time": 14400, + "text": "二", + "punctuation": "," + }, + { + "begin_time": 14400, + "end_time": 14592, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 14592, + "end_time": 14784, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 14784, + "end_time": 14976, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 14976, + "end_time": 15168, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 15168, + "end_time": 15360, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 15360, + "end_time": 15552, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 15552, + "end_time": 15744, + "text": "间", + "punctuation": "" + }, + { + "begin_time": 15744, + "end_time": 15936, + "text": "段", + "punctuation": "" + }, + { + "begin_time": 15936, + "end_time": 16128, + "text": "里", + "punctuation": "" + }, + { + "begin_time": 16128, + "end_time": 16320, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 16320, + "end_time": 16512, + "text": "吧", + "punctuation": "," + }, + { + "begin_time": 16512, + "end_time": 16704, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 16704, + "end_time": 16896, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 16896, + "end_time": 17088, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 17088, + "end_time": 17280, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 17280, + "end_time": 17472, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 17472, + "end_time": 17664, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 17664, + "end_time": 17856, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 17856, + "end_time": 18048, + "text": "月", + "punctuation": "" + }, + { + "begin_time": 18048, + "end_time": 18240, + "text": "之", + "punctuation": "" + }, + { + "begin_time": 18240, + "end_time": 18432, + "text": "内", + "punctuation": "" + }, + { + "begin_time": 18432, + "end_time": 18624, + "text": "他", + "punctuation": "" + }, + { + "begin_time": 18624, + "end_time": 18816, + "text": "消", + "punctuation": "" + }, + { + "begin_time": 18816, + "end_time": 19008, + "text": "他", + "punctuation": "" + }, + { + "begin_time": 19008, + "end_time": 19200, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 19200, + "end_time": 19392, + "text": "工", + "punctuation": "" + }, + { + "begin_time": 19392, + "end_time": 19584, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 19584, + "end_time": 19776, + "text": "消", + "punctuation": "" + }, + { + "begin_time": 19776, + "end_time": 19968, + "text": "耗", + "punctuation": "" + }, + { + "begin_time": 19968, + "end_time": 20160, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 20160, + "end_time": 20352, + "text": "哪", + "punctuation": "" + }, + { + "begin_time": 20352, + "end_time": 20544, + "text": "些", + "punctuation": "" + }, + { + "begin_time": 20544, + "end_time": 20736, + "text": "项", + "punctuation": "" + }, + { + "begin_time": 20736, + "end_time": 20928, + "text": "目", + "punctuation": "" + }, + { + "begin_time": 20928, + "end_time": 21120, + "text": "上", + "punctuation": "。" + } + ] + }, + { + "begin_time": 21120, + "end_time": 27755, + "text": "对,第三个是这十个人在这个一个月就总的这个这大坨工时消耗在哪些时间段里?", + "sentence_id": 7, + "speaker_id": 0, + "words": [ + { + "begin_time": 21120, + "end_time": 21312, + "text": "对", + "punctuation": "," + }, + { + "begin_time": 21312, + "end_time": 21504, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 21504, + "end_time": 21696, + "text": "三", + "punctuation": "" + }, + { + "begin_time": 21696, + "end_time": 21888, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 21888, + "end_time": 22080, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 22080, + "end_time": 22272, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 22272, + "end_time": 22464, + "text": "十", + "punctuation": "" + }, + { + "begin_time": 22464, + "end_time": 22656, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 22656, + "end_time": 22848, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 22848, + "end_time": 23040, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 23040, + "end_time": 23232, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 23232, + "end_time": 23424, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 23424, + "end_time": 23616, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 23616, + "end_time": 23808, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 23808, + "end_time": 24000, + "text": "月", + "punctuation": "" + }, + { + "begin_time": 24000, + "end_time": 24192, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 24192, + "end_time": 24384, + "text": "总", + "punctuation": "" + }, + { + "begin_time": 24384, + "end_time": 24576, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 24576, + "end_time": 24768, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 24768, + "end_time": 24960, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 24960, + "end_time": 25152, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 25152, + "end_time": 25344, + "text": "大", + "punctuation": "" + }, + { + "begin_time": 25344, + "end_time": 25536, + "text": "坨", + "punctuation": "" + }, + { + "begin_time": 25536, + "end_time": 25728, + "text": "工", + "punctuation": "" + }, + { + "begin_time": 25728, + "end_time": 25920, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 25920, + "end_time": 26123, + "text": "消", + "punctuation": "" + }, + { + "begin_time": 26123, + "end_time": 26327, + "text": "耗", + "punctuation": "" + }, + { + "begin_time": 26327, + "end_time": 26531, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 26531, + "end_time": 26735, + "text": "哪", + "punctuation": "" + }, + { + "begin_time": 26735, + "end_time": 26939, + "text": "些", + "punctuation": "" + }, + { + "begin_time": 26939, + "end_time": 27143, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 27143, + "end_time": 27347, + "text": "间", + "punctuation": "" + }, + { + "begin_time": 27347, + "end_time": 27551, + "text": "段", + "punctuation": "" + }, + { + "begin_time": 27551, + "end_time": 27755, + "text": "里", + "punctuation": "?" + } + ] + }, + { + "begin_time": 27755, + "end_time": 33871, + "text": "嗯,呃现在可能就是你说的那第三个还没有找到一个比较好的表达方式。", + "sentence_id": 8, + "speaker_id": 1, + "words": [ + { + "begin_time": 27755, + "end_time": 27958, + "text": "嗯", + "punctuation": "," + }, + { + "begin_time": 27958, + "end_time": 28162, + "text": "呃", + "punctuation": "" + }, + { + "begin_time": 28162, + "end_time": 28366, + "text": "现", + "punctuation": "" + }, + { + "begin_time": 28366, + "end_time": 28570, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 28570, + "end_time": 28774, + "text": "可", + "punctuation": "" + }, + { + "begin_time": 28774, + "end_time": 28978, + "text": "能", + "punctuation": "" + }, + { + "begin_time": 28978, + "end_time": 29182, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 29182, + "end_time": 29386, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 29386, + "end_time": 29590, + "text": "你", + "punctuation": "" + }, + { + "begin_time": 29590, + "end_time": 29793, + "text": "说", + "punctuation": "" + }, + { + "begin_time": 29793, + "end_time": 29997, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 29997, + "end_time": 30201, + "text": "那", + "punctuation": "" + }, + { + "begin_time": 30201, + "end_time": 30405, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 30405, + "end_time": 30609, + "text": "三", + "punctuation": "" + }, + { + "begin_time": 30609, + "end_time": 30813, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 30813, + "end_time": 31017, + "text": "还", + "punctuation": "" + }, + { + "begin_time": 31017, + "end_time": 31221, + "text": "没", + "punctuation": "" + }, + { + "begin_time": 31221, + "end_time": 31425, + "text": "有", + "punctuation": "" + }, + { + "begin_time": 31425, + "end_time": 31628, + "text": "找", + "punctuation": "" + }, + { + "begin_time": 31628, + "end_time": 31832, + "text": "到", + "punctuation": "" + }, + { + "begin_time": 31832, + "end_time": 32036, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 32036, + "end_time": 32240, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 32240, + "end_time": 32444, + "text": "比", + "punctuation": "" + }, + { + "begin_time": 32444, + "end_time": 32648, + "text": "较", + "punctuation": "" + }, + { + "begin_time": 32648, + "end_time": 32852, + "text": "好", + "punctuation": "" + }, + { + "begin_time": 32852, + "end_time": 33056, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 33056, + "end_time": 33260, + "text": "表", + "punctuation": "" + }, + { + "begin_time": 33260, + "end_time": 33463, + "text": "达", + "punctuation": "" + }, + { + "begin_time": 33463, + "end_time": 33667, + "text": "方", + "punctuation": "" + }, + { + "begin_time": 33667, + "end_time": 33871, + "text": "式", + "punctuation": "。" + } + ] + }, + { + "begin_time": 33871, + "end_time": 45085, + "text": "呃,第二个第二个你看第二个就是这个就是我们选择一个人啊,比如说我们选曹冲也可以哈,然后呢这个就是在在这个时间段里,对吧?", + "sentence_id": 9, + "speaker_id": 1, + "words": [ + { + "begin_time": 33871, + "end_time": 34075, + "text": "呃", + "punctuation": "," + }, + { + "begin_time": 34075, + "end_time": 34279, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 34279, + "end_time": 34483, + "text": "二", + "punctuation": "" + }, + { + "begin_time": 34483, + "end_time": 34687, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 34687, + "end_time": 34891, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 34891, + "end_time": 35095, + "text": "二", + "punctuation": "" + }, + { + "begin_time": 35095, + "end_time": 35298, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 35298, + "end_time": 35502, + "text": "你", + "punctuation": "" + }, + { + "begin_time": 35502, + "end_time": 35706, + "text": "看", + "punctuation": "" + }, + { + "begin_time": 35706, + "end_time": 35910, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 35910, + "end_time": 36114, + "text": "二", + "punctuation": "" + }, + { + "begin_time": 36114, + "end_time": 36318, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 36318, + "end_time": 36522, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 36522, + "end_time": 36726, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 36726, + "end_time": 36930, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 36930, + "end_time": 37133, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 37133, + "end_time": 37337, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 37337, + "end_time": 37541, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 37541, + "end_time": 37745, + "text": "我", + "punctuation": "" + }, + { + "begin_time": 37745, + "end_time": 37949, + "text": "们", + "punctuation": "" + }, + { + "begin_time": 37949, + "end_time": 38153, + "text": "选", + "punctuation": "" + }, + { + "begin_time": 38153, + "end_time": 38357, + "text": "择", + "punctuation": "" + }, + { + "begin_time": 38357, + "end_time": 38561, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 38561, + "end_time": 38765, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 38765, + "end_time": 38968, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 38968, + "end_time": 39172, + "text": "啊", + "punctuation": "," + }, + { + "begin_time": 39172, + "end_time": 39376, + "text": "比", + "punctuation": "" + }, + { + "begin_time": 39376, + "end_time": 39580, + "text": "如", + "punctuation": "" + }, + { + "begin_time": 39580, + "end_time": 39784, + "text": "说", + "punctuation": "" + }, + { + "begin_time": 39784, + "end_time": 39988, + "text": "我", + "punctuation": "" + }, + { + "begin_time": 39988, + "end_time": 40192, + "text": "们", + "punctuation": "" + }, + { + "begin_time": 40192, + "end_time": 40396, + "text": "选", + "punctuation": "" + }, + { + "begin_time": 40396, + "end_time": 40600, + "text": "曹", + "punctuation": "" + }, + { + "begin_time": 40600, + "end_time": 40803, + "text": "冲", + "punctuation": "" + }, + { + "begin_time": 40803, + "end_time": 41007, + "text": "也", + "punctuation": "" + }, + { + "begin_time": 41007, + "end_time": 41211, + "text": "可", + "punctuation": "" + }, + { + "begin_time": 41211, + "end_time": 41415, + "text": "以", + "punctuation": "" + }, + { + "begin_time": 41415, + "end_time": 41619, + "text": "哈", + "punctuation": "," + }, + { + "begin_time": 41619, + "end_time": 41823, + "text": "然", + "punctuation": "" + }, + { + "begin_time": 41823, + "end_time": 42027, + "text": "后", + "punctuation": "" + }, + { + "begin_time": 42027, + "end_time": 42231, + "text": "呢", + "punctuation": "" + }, + { + "begin_time": 42231, + "end_time": 42435, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 42435, + "end_time": 42638, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 42638, + "end_time": 42842, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 42842, + "end_time": 43046, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 43046, + "end_time": 43250, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 43250, + "end_time": 43454, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 43454, + "end_time": 43658, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 43658, + "end_time": 43862, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 43862, + "end_time": 44066, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 44066, + "end_time": 44270, + "text": "间", + "punctuation": "" + }, + { + "begin_time": 44270, + "end_time": 44473, + "text": "段", + "punctuation": "" + }, + { + "begin_time": 44473, + "end_time": 44677, + "text": "里", + "punctuation": "," + }, + { + "begin_time": 44677, + "end_time": 44881, + "text": "对", + "punctuation": "" + }, + { + "begin_time": 44881, + "end_time": 45085, + "text": "吧", + "punctuation": "?" + } + ] + }, + { + "begin_time": 45085, + "end_time": 54247, + "text": "比如说他一个月或或者一个月嘛,或者一个月到31号、29号,你看这就可以看。", + "sentence_id": 10, + "speaker_id": 1, + "words": [ + { + "begin_time": 45085, + "end_time": 45289, + "text": "比", + "punctuation": "" + }, + { + "begin_time": 45289, + "end_time": 45493, + "text": "如", + "punctuation": "" + }, + { + "begin_time": 45493, + "end_time": 45697, + "text": "说", + "punctuation": "" + }, + { + "begin_time": 45697, + "end_time": 45901, + "text": "他", + "punctuation": "" + }, + { + "begin_time": 45901, + "end_time": 46105, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 46105, + "end_time": 46308, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 46308, + "end_time": 46512, + "text": "月", + "punctuation": "" + }, + { + "begin_time": 46512, + "end_time": 46716, + "text": "或", + "punctuation": "" + }, + { + "begin_time": 46716, + "end_time": 46920, + "text": "或", + "punctuation": "" + }, + { + "begin_time": 46920, + "end_time": 47124, + "text": "者", + "punctuation": "" + }, + { + "begin_time": 47124, + "end_time": 47328, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 47328, + "end_time": 47532, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 47532, + "end_time": 47736, + "text": "月", + "punctuation": "" + }, + { + "begin_time": 47736, + "end_time": 47940, + "text": "嘛", + "punctuation": "," + }, + { + "begin_time": 48420, + "end_time": 48697, + "text": "或", + "punctuation": "" + }, + { + "begin_time": 48697, + "end_time": 48975, + "text": "者", + "punctuation": "" + }, + { + "begin_time": 48975, + "end_time": 49252, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 49252, + "end_time": 49530, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 49530, + "end_time": 49807, + "text": "月", + "punctuation": "" + }, + { + "begin_time": 49807, + "end_time": 50085, + "text": "到", + "punctuation": "" + }, + { + "begin_time": 50085, + "end_time": 51195, + "text": "31号", + "punctuation": "、" + }, + { + "begin_time": 51195, + "end_time": 52305, + "text": "29号", + "punctuation": "," + }, + { + "begin_time": 52305, + "end_time": 52582, + "text": "你", + "punctuation": "" + }, + { + "begin_time": 52582, + "end_time": 52860, + "text": "看", + "punctuation": "" + }, + { + "begin_time": 52860, + "end_time": 53137, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 53137, + "end_time": 53415, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 53415, + "end_time": 53692, + "text": "可", + "punctuation": "" + }, + { + "begin_time": 53692, + "end_time": 53970, + "text": "以", + "punctuation": "" + }, + { + "begin_time": 53970, + "end_time": 54247, + "text": "看", + "punctuation": "。" + } + ] + }, + { + "begin_time": 54247, + "end_time": 55357, + "text": "出长春这。", + "sentence_id": 11, + "speaker_id": 0, + "words": [ + { + "begin_time": 54247, + "end_time": 54525, + "text": "出", + "punctuation": "" + }, + { + "begin_time": 54525, + "end_time": 54802, + "text": "长", + "punctuation": "" + }, + { + "begin_time": 54802, + "end_time": 55080, + "text": "春", + "punctuation": "" + }, + { + "begin_time": 55080, + "end_time": 55357, + "text": "这", + "punctuation": "。" + } + ] + }, + { + "begin_time": 55357, + "end_time": 57300, + "text": "两天没,不好意思。", + "sentence_id": 12, + "speaker_id": 1, + "words": [ + { + "begin_time": 55357, + "end_time": 55635, + "text": "两", + "punctuation": "" + }, + { + "begin_time": 55635, + "end_time": 55912, + "text": "天", + "punctuation": "" + }, + { + "begin_time": 55912, + "end_time": 56190, + "text": "没", + "punctuation": "," + }, + { + "begin_time": 56190, + "end_time": 56467, + "text": "不", + "punctuation": "" + }, + { + "begin_time": 56467, + "end_time": 56745, + "text": "好", + "punctuation": "" + }, + { + "begin_time": 56745, + "end_time": 57022, + "text": "意", + "punctuation": "" + }, + { + "begin_time": 57022, + "end_time": 57300, + "text": "思", + "punctuation": "。" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 8e764a7..a552ffd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ mysql-connector-python uvicorn[standard] python-multipart pydantic[email] -passlib[bcrypt] \ No newline at end of file +passlib[bcrypt] +qiniu \ No newline at end of file diff --git a/test_full_upload.py b/test_full_upload.py new file mode 100644 index 0000000..567afdf --- /dev/null +++ b/test_full_upload.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +import os +import sys +import asyncio +from pathlib import Path + +# Add the app directory to the path +sys.path.append(os.path.join(os.path.dirname(__file__), 'app')) + +# Mock the FastAPI UploadFile +class MockUploadFile: + def __init__(self, filename, content): + self.filename = filename + self.content = content + self.size = len(content) + self._file_pos = 0 + + async def read(self, size=-1): + if size == -1: + result = self.content[self._file_pos:] + self._file_pos = len(self.content) + else: + result = self.content[self._file_pos:self._file_pos + size] + self._file_pos += len(result) + return result + + def file(self): + from io import BytesIO + return BytesIO(self.content.encode() if isinstance(self.content, str) else self.content) + +async def test_audio_upload(): + from app.api.endpoints.meetings import AUDIO_DIR + from app.services.qiniu_service import qiniu_service + + # Path to the problematic audio file + audio_file_path = "/Users/jiliu/工作/projects/imeeting/backend/uploads/audio/31ce039a-f619-4869-91c8-eab934bbd1d4.m4a" + + # Read the content of the audio file + try: + with open(audio_file_path, "rb") as f: + test_content = f.read() + print(f"Successfully read content from {audio_file_path}") + except FileNotFoundError: + print(f"Error: The file was not found at {audio_file_path}") + return + + # Create mock UploadFile with the real audio content + mock_file = MockUploadFile("31ce039a-f619-4869-91c8-eab934bbd1d4.m4a", test_content) + + # Create temporary file for upload (simulating the API endpoint) + file_extension = ".m4a" + from uuid import uuid4 + temp_filename = f"{uuid4()}{file_extension}" + temp_path = AUDIO_DIR / temp_filename + + print(f"Creating temporary file at: {temp_path}") + + # Save file temporarily (simulating the API endpoint) + try: + # Simulate shutil.copyfileobj(mock_file.file(), open(temp_path, "wb")) + with open(temp_path, "wb") as buffer: + buffer.write(mock_file.content) # content is already bytes + + print(f"Temporary file created successfully. Exists: {temp_path.exists()}") + print(f"Temporary file size: {temp_path.stat().st_size}") + except Exception as e: + print(f"Failed to save temporary file: {str(e)}") + return + + # Test upload to Qiniu (simulating the API endpoint) + try: + print(f"Attempting to upload audio to Qiniu...") + print(f"Temp file path: {temp_path}") + print(f"Temp file exists: {temp_path.exists()}") + + success, qiniu_url, error_msg = qiniu_service.upload_audio_file( + str(temp_path), 11, "test-audio.mp3" + ) + + print(f"Qiniu upload result - success: {success}") + print(f"Qiniu upload result - url: {qiniu_url}") + print(f"Qiniu upload result - error: {error_msg}") + + # Clean up temporary file + if temp_path.exists(): + temp_path.unlink() + print("Temporary file cleaned up") + + if success: + print("Upload successful!") + print(f"URL: {qiniu_url}") + else: + print(f"Upload failed: {error_msg}") + + except Exception as e: + print(f"Exception in audio upload: {str(e)}") + import traceback + print(f"Traceback: {traceback.format_exc()}") + # Clean up temporary file in case of error + if temp_path.exists(): + temp_path.unlink() + +if __name__ == "__main__": + asyncio.run(test_audio_upload()) \ No newline at end of file diff --git a/test_qiniu.py b/test_qiniu.py new file mode 100644 index 0000000..f459a8e --- /dev/null +++ b/test_qiniu.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import sys +from qiniu import Auth, put_file_v2, BucketManager + +# Add app path +sys.path.append(os.path.join(os.path.dirname(__file__), 'app')) + +from app.core.config import QINIU_ACCESS_KEY, QINIU_SECRET_KEY, QINIU_BUCKET, QINIU_DOMAIN + +def test_qiniu_connection(): + print("=== 七牛云连接测试 ===") + print(f"Access Key: {QINIU_ACCESS_KEY[:10]}...") + print(f"Secret Key: {QINIU_SECRET_KEY[:10]}...") + print(f"Bucket: {QINIU_BUCKET}") + print(f"Domain: {QINIU_DOMAIN}") + + # 创建认证对象 + q = Auth(QINIU_ACCESS_KEY, QINIU_SECRET_KEY) + + # 测试1: 生成上传token + try: + key = "test/connection-test.txt" + token = q.upload_token(QINIU_BUCKET, key, 3600) + print(f"✓ Token生成成功: {token[:50]}...") + except Exception as e: + print(f"✗ Token生成失败: {e}") + return False + + # 测试2: 列举存储空间 (测试认证是否正确) + try: + bucket_manager = BucketManager(q) + ret, eof, info = bucket_manager.list(QINIU_BUCKET, limit=100) + print(f"✓ Bucket访问成功, status_code: {info.status_code}") + if ret: + print(f" 存储空间中有文件: {len(ret.get('items', []))} 个") + except Exception as e: + print(f"✗ Bucket访问失败: {e}") + return False + + # 测试3: 上传一个小文件 + test_file = "/Users/jiliu/工作/projects/imeeting/backend/uploads/result.json" + if os.path.exists(test_file): + try: + key = "test/result1.json" + token = q.upload_token(QINIU_BUCKET, key, 3600) + ret, info = put_file_v2(token, key, test_file, version='v2') + + print(f"上传结果:") + print(f" ret: {ret}") + print(f" status_code: {info.status_code}") + print(f" text_body: {info.text_body}") + print(f" url: {info.url}") + print(f" req_id: {info.req_id}") + print(f" x_log: {info.x_log}") + + if info.status_code == 200: + print("✓ 文件上传成功") + url = f"http://{QINIU_DOMAIN}/{key}" + print(f" 访问URL: {url}") + return True + else: + print(f"✗ 文件上传失败: {info.status_code}") + return False + + except Exception as e: + print(f"✗ 文件上传异常: {e}") + import traceback + traceback.print_exc() + return False + else: + print(f"✗ 测试文件不存在: {test_file}") + return False + +if __name__ == "__main__": + success = test_qiniu_connection() + if success: + print("\n🎉 七牛云连接测试成功!") + else: + print("\n❌ 七牛云连接测试失败!") \ No newline at end of file diff --git a/test_upload.py b/test_upload.py new file mode 100644 index 0000000..5f091bf --- /dev/null +++ b/test_upload.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# flake8: noqa +from qiniu import Auth, put_data ,put_file_v2, etag +import qiniu.config +#需要填写你的 Access Key 和 Secret Key +access_key = 'A0tp96HCtg-wZCughTgi5vc2pJnw3btClwxRE_e8' +secret_key = 'Lj-MSHpaVbmzpS86kMIjmwikvYOT9iPBjCk9hm6k' +#构建鉴权对象 +q = Auth(access_key, secret_key) +#要上传的空间 +bucket_name = 'imeeting' +#上传后保存的文件名 +key = 'test/result.json' +#生成上传 Token,可以指定过期时间等 +token = q.upload_token(bucket_name, key, 3600) +#要上传文件的本地路径 +localfile = './uploads/result.json' +ret, info = put_file_v2(token, key, localfile, version='v2') +print(info) +assert ret['key'] == key +assert ret['hash'] == etag(localfile) \ No newline at end of file diff --git a/uploads/.DS_Store b/uploads/.DS_Store index 336ea78..2396b3c 100644 Binary files a/uploads/.DS_Store and b/uploads/.DS_Store differ diff --git a/uploads/audio/.DS_Store b/uploads/audio/.DS_Store new file mode 100644 index 0000000..b70e4af Binary files /dev/null and b/uploads/audio/.DS_Store differ diff --git a/uploads/audio/28/919bd719-9e06-4aa7-934c-b8b04309c928.mp3 b/uploads/audio/28/919bd719-9e06-4aa7-934c-b8b04309c928.mp3 new file mode 100644 index 0000000..0fd96c9 Binary files /dev/null and b/uploads/audio/28/919bd719-9e06-4aa7-934c-b8b04309c928.mp3 differ diff --git a/uploads/audio/29/06814a7f-8dca-489c-8176-b66899c8f858.mp3 b/uploads/audio/29/06814a7f-8dca-489c-8176-b66899c8f858.mp3 new file mode 100644 index 0000000..0fd96c9 Binary files /dev/null and b/uploads/audio/29/06814a7f-8dca-489c-8176-b66899c8f858.mp3 differ diff --git a/uploads/audio/31/33a56d56-b651-44de-9c6d-487992c0c052.mp3 b/uploads/audio/31/33a56d56-b651-44de-9c6d-487992c0c052.mp3 new file mode 100644 index 0000000..0fd96c9 Binary files /dev/null and b/uploads/audio/31/33a56d56-b651-44de-9c6d-487992c0c052.mp3 differ diff --git a/uploads/audio/32/fec5835c-2797-40aa-85cd-a5e62d01416a.mp3 b/uploads/audio/32/fec5835c-2797-40aa-85cd-a5e62d01416a.mp3 new file mode 100644 index 0000000..0fd96c9 Binary files /dev/null and b/uploads/audio/32/fec5835c-2797-40aa-85cd-a5e62d01416a.mp3 differ diff --git a/uploads/audio/33/770aadf6-03ec-4600-a8ae-95436ab1e46d.mp3 b/uploads/audio/33/770aadf6-03ec-4600-a8ae-95436ab1e46d.mp3 new file mode 100644 index 0000000..0fd96c9 Binary files /dev/null and b/uploads/audio/33/770aadf6-03ec-4600-a8ae-95436ab1e46d.mp3 differ diff --git a/uploads/audio/34/6a9b798f-30ef-4acb-b253-31fc40f0ecab.mp3 b/uploads/audio/34/6a9b798f-30ef-4acb-b253-31fc40f0ecab.mp3 new file mode 100644 index 0000000..0fd96c9 Binary files /dev/null and b/uploads/audio/34/6a9b798f-30ef-4acb-b253-31fc40f0ecab.mp3 differ diff --git a/uploads/audio/35/3c35841f-c872-4f90-a408-560f47078bfc.mp3 b/uploads/audio/35/3c35841f-c872-4f90-a408-560f47078bfc.mp3 new file mode 100644 index 0000000..0fd96c9 Binary files /dev/null and b/uploads/audio/35/3c35841f-c872-4f90-a408-560f47078bfc.mp3 differ diff --git a/uploads/audio/36/80f305d1-843f-4286-89de-13333063ff20.mp3 b/uploads/audio/36/80f305d1-843f-4286-89de-13333063ff20.mp3 new file mode 100644 index 0000000..0fd96c9 Binary files /dev/null and b/uploads/audio/36/80f305d1-843f-4286-89de-13333063ff20.mp3 differ diff --git a/uploads/audio/37/317c75e3-ba72-45b8-9f07-c2835491efb9.mp3 b/uploads/audio/37/317c75e3-ba72-45b8-9f07-c2835491efb9.mp3 new file mode 100644 index 0000000..0fd96c9 Binary files /dev/null and b/uploads/audio/37/317c75e3-ba72-45b8-9f07-c2835491efb9.mp3 differ diff --git a/uploads/markdown/.DS_Store b/uploads/markdown/.DS_Store new file mode 100644 index 0000000..df22303 Binary files /dev/null and b/uploads/markdown/.DS_Store differ diff --git a/uploads/markdown/30/bc30d102-8363-458e-aa69-23ff9913857d.jpg b/uploads/markdown/30/bc30d102-8363-458e-aa69-23ff9913857d.jpg new file mode 100644 index 0000000..486797f Binary files /dev/null and b/uploads/markdown/30/bc30d102-8363-458e-aa69-23ff9913857d.jpg differ diff --git a/uploads/markdown/35/ecd759af-037a-4d75-9cd5-f89a86feac3a.png b/uploads/markdown/35/ecd759af-037a-4d75-9cd5-f89a86feac3a.png new file mode 100644 index 0000000..61eaac8 Binary files /dev/null and b/uploads/markdown/35/ecd759af-037a-4d75-9cd5-f89a86feac3a.png differ diff --git a/uploads/markdown/35/f4e284d7-b3ec-48f2-86ca-c72aa21096cc.jpg b/uploads/markdown/35/f4e284d7-b3ec-48f2-86ca-c72aa21096cc.jpg new file mode 100644 index 0000000..bc6cd9a Binary files /dev/null and b/uploads/markdown/35/f4e284d7-b3ec-48f2-86ca-c72aa21096cc.jpg differ diff --git a/uploads/meeting_records_2.mp3 b/uploads/meeting_records_2.mp3 new file mode 100644 index 0000000..0fd96c9 Binary files /dev/null and b/uploads/meeting_records_2.mp3 differ diff --git a/uploads/r.txt b/uploads/r.txt new file mode 100644 index 0000000..a552ffd --- /dev/null +++ b/uploads/r.txt @@ -0,0 +1,7 @@ +fastapi +mysql-connector-python +uvicorn[standard] +python-multipart +pydantic[email] +passlib[bcrypt] +qiniu \ No newline at end of file diff --git a/uploads/result.json b/uploads/result.json new file mode 100644 index 0000000..0788712 --- /dev/null +++ b/uploads/result.json @@ -0,0 +1,1707 @@ +{ + "file_url": "http://t0vogyxkz.hn-bkt.clouddn.com/records/meeting_records_1.mp3", + "properties": { + "audio_format": "mp3", + "channels": [ + 0, + 1 + ], + "original_sampling_rate": 44100, + "original_duration_in_milliseconds": 60000 + }, + "transcripts": [ + { + "channel_id": 0, + "content_duration_in_milliseconds": 57300, + "text": "卖了工时是吧?因为这项目消耗了哪些人的工时?嗯,因为你这个项目一出来,这项目比如说100个工时,对不对?嗯,那自然就是这项目的100工时消耗在哪人身上,是吧?这是第一个。第二,这一个人在时间段里是吧,这一个人在一个月之内他消他的工时消耗在哪些项目上。对,第三个是这十个人在这个一个月就总的这个这大坨工时消耗在哪些时间段里?嗯,呃现在可能就是你说的那第三个还没有找到一个比较好的表达方式。呃,第二个第二个你看第二个就是这个就是我们选择一个人啊,比如说我们选曹冲也可以哈,然后呢这个就是在在这个时间段里,对吧?比如说他一个月或或者一个月嘛,或者一个月到31号、29号,你看这就可以看。出长春这。两天没,不好意思。", + "sentences": [ + { + "begin_time": 0, + "end_time": 1152, + "text": "卖了工时是吧?", + "sentence_id": 1, + "speaker_id": 0, + "words": [ + { + "begin_time": 0, + "end_time": 192, + "text": "卖", + "punctuation": "" + }, + { + "begin_time": 192, + "end_time": 384, + "text": "了", + "punctuation": "" + }, + { + "begin_time": 384, + "end_time": 576, + "text": "工", + "punctuation": "" + }, + { + "begin_time": 576, + "end_time": 768, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 768, + "end_time": 960, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 960, + "end_time": 1152, + "text": "吧", + "punctuation": "?" + } + ] + }, + { + "begin_time": 1152, + "end_time": 3840, + "text": "因为这项目消耗了哪些人的工时?", + "sentence_id": 2, + "speaker_id": 0, + "words": [ + { + "begin_time": 1152, + "end_time": 1344, + "text": "因", + "punctuation": "" + }, + { + "begin_time": 1344, + "end_time": 1536, + "text": "为", + "punctuation": "" + }, + { + "begin_time": 1536, + "end_time": 1728, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 1728, + "end_time": 1920, + "text": "项", + "punctuation": "" + }, + { + "begin_time": 1920, + "end_time": 2112, + "text": "目", + "punctuation": "" + }, + { + "begin_time": 2112, + "end_time": 2304, + "text": "消", + "punctuation": "" + }, + { + "begin_time": 2304, + "end_time": 2496, + "text": "耗", + "punctuation": "" + }, + { + "begin_time": 2496, + "end_time": 2688, + "text": "了", + "punctuation": "" + }, + { + "begin_time": 2688, + "end_time": 2880, + "text": "哪", + "punctuation": "" + }, + { + "begin_time": 2880, + "end_time": 3072, + "text": "些", + "punctuation": "" + }, + { + "begin_time": 3072, + "end_time": 3264, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 3264, + "end_time": 3456, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 3456, + "end_time": 3648, + "text": "工", + "punctuation": "" + }, + { + "begin_time": 3648, + "end_time": 3840, + "text": "时", + "punctuation": "?" + } + ] + }, + { + "begin_time": 3840, + "end_time": 8640, + "text": "嗯,因为你这个项目一出来,这项目比如说100个工时,对不对?", + "sentence_id": 3, + "speaker_id": 0, + "words": [ + { + "begin_time": 3840, + "end_time": 4032, + "text": "嗯", + "punctuation": "," + }, + { + "begin_time": 4032, + "end_time": 4224, + "text": "因", + "punctuation": "" + }, + { + "begin_time": 4224, + "end_time": 4416, + "text": "为", + "punctuation": "" + }, + { + "begin_time": 4416, + "end_time": 4608, + "text": "你", + "punctuation": "" + }, + { + "begin_time": 4608, + "end_time": 4800, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 4800, + "end_time": 4992, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 4992, + "end_time": 5184, + "text": "项", + "punctuation": "" + }, + { + "begin_time": 5184, + "end_time": 5376, + "text": "目", + "punctuation": "" + }, + { + "begin_time": 5376, + "end_time": 5568, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 5568, + "end_time": 5760, + "text": "出", + "punctuation": "" + }, + { + "begin_time": 5760, + "end_time": 5952, + "text": "来", + "punctuation": "," + }, + { + "begin_time": 5952, + "end_time": 6144, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 6144, + "end_time": 6336, + "text": "项", + "punctuation": "" + }, + { + "begin_time": 6336, + "end_time": 6528, + "text": "目", + "punctuation": "" + }, + { + "begin_time": 6528, + "end_time": 6720, + "text": "比", + "punctuation": "" + }, + { + "begin_time": 6720, + "end_time": 6912, + "text": "如", + "punctuation": "" + }, + { + "begin_time": 6912, + "end_time": 7104, + "text": "说", + "punctuation": "" + }, + { + "begin_time": 7104, + "end_time": 7872, + "text": "100个工", + "punctuation": "" + }, + { + "begin_time": 7872, + "end_time": 8064, + "text": "时", + "punctuation": "," + }, + { + "begin_time": 8064, + "end_time": 8256, + "text": "对", + "punctuation": "" + }, + { + "begin_time": 8256, + "end_time": 8448, + "text": "不", + "punctuation": "" + }, + { + "begin_time": 8448, + "end_time": 8640, + "text": "对", + "punctuation": "?" + } + ] + }, + { + "begin_time": 8640, + "end_time": 13056, + "text": "嗯,那自然就是这项目的100工时消耗在哪人身上,是吧?", + "sentence_id": 4, + "speaker_id": 0, + "words": [ + { + "begin_time": 8640, + "end_time": 8832, + "text": "嗯", + "punctuation": "," + }, + { + "begin_time": 8832, + "end_time": 9024, + "text": "那", + "punctuation": "" + }, + { + "begin_time": 9024, + "end_time": 9216, + "text": "自", + "punctuation": "" + }, + { + "begin_time": 9216, + "end_time": 9408, + "text": "然", + "punctuation": "" + }, + { + "begin_time": 9408, + "end_time": 9600, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 9600, + "end_time": 9792, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 9792, + "end_time": 9984, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 9984, + "end_time": 10176, + "text": "项", + "punctuation": "" + }, + { + "begin_time": 10176, + "end_time": 10368, + "text": "目", + "punctuation": "" + }, + { + "begin_time": 10368, + "end_time": 10560, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 10560, + "end_time": 11328, + "text": "100工时", + "punctuation": "" + }, + { + "begin_time": 11328, + "end_time": 11520, + "text": "消", + "punctuation": "" + }, + { + "begin_time": 11520, + "end_time": 11712, + "text": "耗", + "punctuation": "" + }, + { + "begin_time": 11712, + "end_time": 11904, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 11904, + "end_time": 12096, + "text": "哪", + "punctuation": "" + }, + { + "begin_time": 12096, + "end_time": 12288, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 12288, + "end_time": 12480, + "text": "身", + "punctuation": "" + }, + { + "begin_time": 12480, + "end_time": 12672, + "text": "上", + "punctuation": "," + }, + { + "begin_time": 12672, + "end_time": 12864, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 12864, + "end_time": 13056, + "text": "吧", + "punctuation": "?" + } + ] + }, + { + "begin_time": 13056, + "end_time": 14016, + "text": "这是第一个。", + "sentence_id": 5, + "speaker_id": 0, + "words": [ + { + "begin_time": 13056, + "end_time": 13248, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 13248, + "end_time": 13440, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 13440, + "end_time": 13632, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 13632, + "end_time": 13824, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 13824, + "end_time": 14016, + "text": "个", + "punctuation": "。" + } + ] + }, + { + "begin_time": 14016, + "end_time": 21120, + "text": "第二,这一个人在时间段里是吧,这一个人在一个月之内他消他的工时消耗在哪些项目上。", + "sentence_id": 6, + "speaker_id": 0, + "words": [ + { + "begin_time": 14016, + "end_time": 14208, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 14208, + "end_time": 14400, + "text": "二", + "punctuation": "," + }, + { + "begin_time": 14400, + "end_time": 14592, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 14592, + "end_time": 14784, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 14784, + "end_time": 14976, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 14976, + "end_time": 15168, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 15168, + "end_time": 15360, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 15360, + "end_time": 15552, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 15552, + "end_time": 15744, + "text": "间", + "punctuation": "" + }, + { + "begin_time": 15744, + "end_time": 15936, + "text": "段", + "punctuation": "" + }, + { + "begin_time": 15936, + "end_time": 16128, + "text": "里", + "punctuation": "" + }, + { + "begin_time": 16128, + "end_time": 16320, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 16320, + "end_time": 16512, + "text": "吧", + "punctuation": "," + }, + { + "begin_time": 16512, + "end_time": 16704, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 16704, + "end_time": 16896, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 16896, + "end_time": 17088, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 17088, + "end_time": 17280, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 17280, + "end_time": 17472, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 17472, + "end_time": 17664, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 17664, + "end_time": 17856, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 17856, + "end_time": 18048, + "text": "月", + "punctuation": "" + }, + { + "begin_time": 18048, + "end_time": 18240, + "text": "之", + "punctuation": "" + }, + { + "begin_time": 18240, + "end_time": 18432, + "text": "内", + "punctuation": "" + }, + { + "begin_time": 18432, + "end_time": 18624, + "text": "他", + "punctuation": "" + }, + { + "begin_time": 18624, + "end_time": 18816, + "text": "消", + "punctuation": "" + }, + { + "begin_time": 18816, + "end_time": 19008, + "text": "他", + "punctuation": "" + }, + { + "begin_time": 19008, + "end_time": 19200, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 19200, + "end_time": 19392, + "text": "工", + "punctuation": "" + }, + { + "begin_time": 19392, + "end_time": 19584, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 19584, + "end_time": 19776, + "text": "消", + "punctuation": "" + }, + { + "begin_time": 19776, + "end_time": 19968, + "text": "耗", + "punctuation": "" + }, + { + "begin_time": 19968, + "end_time": 20160, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 20160, + "end_time": 20352, + "text": "哪", + "punctuation": "" + }, + { + "begin_time": 20352, + "end_time": 20544, + "text": "些", + "punctuation": "" + }, + { + "begin_time": 20544, + "end_time": 20736, + "text": "项", + "punctuation": "" + }, + { + "begin_time": 20736, + "end_time": 20928, + "text": "目", + "punctuation": "" + }, + { + "begin_time": 20928, + "end_time": 21120, + "text": "上", + "punctuation": "。" + } + ] + }, + { + "begin_time": 21120, + "end_time": 27755, + "text": "对,第三个是这十个人在这个一个月就总的这个这大坨工时消耗在哪些时间段里?", + "sentence_id": 7, + "speaker_id": 0, + "words": [ + { + "begin_time": 21120, + "end_time": 21312, + "text": "对", + "punctuation": "," + }, + { + "begin_time": 21312, + "end_time": 21504, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 21504, + "end_time": 21696, + "text": "三", + "punctuation": "" + }, + { + "begin_time": 21696, + "end_time": 21888, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 21888, + "end_time": 22080, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 22080, + "end_time": 22272, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 22272, + "end_time": 22464, + "text": "十", + "punctuation": "" + }, + { + "begin_time": 22464, + "end_time": 22656, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 22656, + "end_time": 22848, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 22848, + "end_time": 23040, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 23040, + "end_time": 23232, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 23232, + "end_time": 23424, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 23424, + "end_time": 23616, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 23616, + "end_time": 23808, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 23808, + "end_time": 24000, + "text": "月", + "punctuation": "" + }, + { + "begin_time": 24000, + "end_time": 24192, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 24192, + "end_time": 24384, + "text": "总", + "punctuation": "" + }, + { + "begin_time": 24384, + "end_time": 24576, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 24576, + "end_time": 24768, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 24768, + "end_time": 24960, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 24960, + "end_time": 25152, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 25152, + "end_time": 25344, + "text": "大", + "punctuation": "" + }, + { + "begin_time": 25344, + "end_time": 25536, + "text": "坨", + "punctuation": "" + }, + { + "begin_time": 25536, + "end_time": 25728, + "text": "工", + "punctuation": "" + }, + { + "begin_time": 25728, + "end_time": 25920, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 25920, + "end_time": 26123, + "text": "消", + "punctuation": "" + }, + { + "begin_time": 26123, + "end_time": 26327, + "text": "耗", + "punctuation": "" + }, + { + "begin_time": 26327, + "end_time": 26531, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 26531, + "end_time": 26735, + "text": "哪", + "punctuation": "" + }, + { + "begin_time": 26735, + "end_time": 26939, + "text": "些", + "punctuation": "" + }, + { + "begin_time": 26939, + "end_time": 27143, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 27143, + "end_time": 27347, + "text": "间", + "punctuation": "" + }, + { + "begin_time": 27347, + "end_time": 27551, + "text": "段", + "punctuation": "" + }, + { + "begin_time": 27551, + "end_time": 27755, + "text": "里", + "punctuation": "?" + } + ] + }, + { + "begin_time": 27755, + "end_time": 33871, + "text": "嗯,呃现在可能就是你说的那第三个还没有找到一个比较好的表达方式。", + "sentence_id": 8, + "speaker_id": 1, + "words": [ + { + "begin_time": 27755, + "end_time": 27958, + "text": "嗯", + "punctuation": "," + }, + { + "begin_time": 27958, + "end_time": 28162, + "text": "呃", + "punctuation": "" + }, + { + "begin_time": 28162, + "end_time": 28366, + "text": "现", + "punctuation": "" + }, + { + "begin_time": 28366, + "end_time": 28570, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 28570, + "end_time": 28774, + "text": "可", + "punctuation": "" + }, + { + "begin_time": 28774, + "end_time": 28978, + "text": "能", + "punctuation": "" + }, + { + "begin_time": 28978, + "end_time": 29182, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 29182, + "end_time": 29386, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 29386, + "end_time": 29590, + "text": "你", + "punctuation": "" + }, + { + "begin_time": 29590, + "end_time": 29793, + "text": "说", + "punctuation": "" + }, + { + "begin_time": 29793, + "end_time": 29997, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 29997, + "end_time": 30201, + "text": "那", + "punctuation": "" + }, + { + "begin_time": 30201, + "end_time": 30405, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 30405, + "end_time": 30609, + "text": "三", + "punctuation": "" + }, + { + "begin_time": 30609, + "end_time": 30813, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 30813, + "end_time": 31017, + "text": "还", + "punctuation": "" + }, + { + "begin_time": 31017, + "end_time": 31221, + "text": "没", + "punctuation": "" + }, + { + "begin_time": 31221, + "end_time": 31425, + "text": "有", + "punctuation": "" + }, + { + "begin_time": 31425, + "end_time": 31628, + "text": "找", + "punctuation": "" + }, + { + "begin_time": 31628, + "end_time": 31832, + "text": "到", + "punctuation": "" + }, + { + "begin_time": 31832, + "end_time": 32036, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 32036, + "end_time": 32240, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 32240, + "end_time": 32444, + "text": "比", + "punctuation": "" + }, + { + "begin_time": 32444, + "end_time": 32648, + "text": "较", + "punctuation": "" + }, + { + "begin_time": 32648, + "end_time": 32852, + "text": "好", + "punctuation": "" + }, + { + "begin_time": 32852, + "end_time": 33056, + "text": "的", + "punctuation": "" + }, + { + "begin_time": 33056, + "end_time": 33260, + "text": "表", + "punctuation": "" + }, + { + "begin_time": 33260, + "end_time": 33463, + "text": "达", + "punctuation": "" + }, + { + "begin_time": 33463, + "end_time": 33667, + "text": "方", + "punctuation": "" + }, + { + "begin_time": 33667, + "end_time": 33871, + "text": "式", + "punctuation": "。" + } + ] + }, + { + "begin_time": 33871, + "end_time": 45085, + "text": "呃,第二个第二个你看第二个就是这个就是我们选择一个人啊,比如说我们选曹冲也可以哈,然后呢这个就是在在这个时间段里,对吧?", + "sentence_id": 9, + "speaker_id": 1, + "words": [ + { + "begin_time": 33871, + "end_time": 34075, + "text": "呃", + "punctuation": "," + }, + { + "begin_time": 34075, + "end_time": 34279, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 34279, + "end_time": 34483, + "text": "二", + "punctuation": "" + }, + { + "begin_time": 34483, + "end_time": 34687, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 34687, + "end_time": 34891, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 34891, + "end_time": 35095, + "text": "二", + "punctuation": "" + }, + { + "begin_time": 35095, + "end_time": 35298, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 35298, + "end_time": 35502, + "text": "你", + "punctuation": "" + }, + { + "begin_time": 35502, + "end_time": 35706, + "text": "看", + "punctuation": "" + }, + { + "begin_time": 35706, + "end_time": 35910, + "text": "第", + "punctuation": "" + }, + { + "begin_time": 35910, + "end_time": 36114, + "text": "二", + "punctuation": "" + }, + { + "begin_time": 36114, + "end_time": 36318, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 36318, + "end_time": 36522, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 36522, + "end_time": 36726, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 36726, + "end_time": 36930, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 36930, + "end_time": 37133, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 37133, + "end_time": 37337, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 37337, + "end_time": 37541, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 37541, + "end_time": 37745, + "text": "我", + "punctuation": "" + }, + { + "begin_time": 37745, + "end_time": 37949, + "text": "们", + "punctuation": "" + }, + { + "begin_time": 37949, + "end_time": 38153, + "text": "选", + "punctuation": "" + }, + { + "begin_time": 38153, + "end_time": 38357, + "text": "择", + "punctuation": "" + }, + { + "begin_time": 38357, + "end_time": 38561, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 38561, + "end_time": 38765, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 38765, + "end_time": 38968, + "text": "人", + "punctuation": "" + }, + { + "begin_time": 38968, + "end_time": 39172, + "text": "啊", + "punctuation": "," + }, + { + "begin_time": 39172, + "end_time": 39376, + "text": "比", + "punctuation": "" + }, + { + "begin_time": 39376, + "end_time": 39580, + "text": "如", + "punctuation": "" + }, + { + "begin_time": 39580, + "end_time": 39784, + "text": "说", + "punctuation": "" + }, + { + "begin_time": 39784, + "end_time": 39988, + "text": "我", + "punctuation": "" + }, + { + "begin_time": 39988, + "end_time": 40192, + "text": "们", + "punctuation": "" + }, + { + "begin_time": 40192, + "end_time": 40396, + "text": "选", + "punctuation": "" + }, + { + "begin_time": 40396, + "end_time": 40600, + "text": "曹", + "punctuation": "" + }, + { + "begin_time": 40600, + "end_time": 40803, + "text": "冲", + "punctuation": "" + }, + { + "begin_time": 40803, + "end_time": 41007, + "text": "也", + "punctuation": "" + }, + { + "begin_time": 41007, + "end_time": 41211, + "text": "可", + "punctuation": "" + }, + { + "begin_time": 41211, + "end_time": 41415, + "text": "以", + "punctuation": "" + }, + { + "begin_time": 41415, + "end_time": 41619, + "text": "哈", + "punctuation": "," + }, + { + "begin_time": 41619, + "end_time": 41823, + "text": "然", + "punctuation": "" + }, + { + "begin_time": 41823, + "end_time": 42027, + "text": "后", + "punctuation": "" + }, + { + "begin_time": 42027, + "end_time": 42231, + "text": "呢", + "punctuation": "" + }, + { + "begin_time": 42231, + "end_time": 42435, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 42435, + "end_time": 42638, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 42638, + "end_time": 42842, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 42842, + "end_time": 43046, + "text": "是", + "punctuation": "" + }, + { + "begin_time": 43046, + "end_time": 43250, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 43250, + "end_time": 43454, + "text": "在", + "punctuation": "" + }, + { + "begin_time": 43454, + "end_time": 43658, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 43658, + "end_time": 43862, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 43862, + "end_time": 44066, + "text": "时", + "punctuation": "" + }, + { + "begin_time": 44066, + "end_time": 44270, + "text": "间", + "punctuation": "" + }, + { + "begin_time": 44270, + "end_time": 44473, + "text": "段", + "punctuation": "" + }, + { + "begin_time": 44473, + "end_time": 44677, + "text": "里", + "punctuation": "," + }, + { + "begin_time": 44677, + "end_time": 44881, + "text": "对", + "punctuation": "" + }, + { + "begin_time": 44881, + "end_time": 45085, + "text": "吧", + "punctuation": "?" + } + ] + }, + { + "begin_time": 45085, + "end_time": 54247, + "text": "比如说他一个月或或者一个月嘛,或者一个月到31号、29号,你看这就可以看。", + "sentence_id": 10, + "speaker_id": 1, + "words": [ + { + "begin_time": 45085, + "end_time": 45289, + "text": "比", + "punctuation": "" + }, + { + "begin_time": 45289, + "end_time": 45493, + "text": "如", + "punctuation": "" + }, + { + "begin_time": 45493, + "end_time": 45697, + "text": "说", + "punctuation": "" + }, + { + "begin_time": 45697, + "end_time": 45901, + "text": "他", + "punctuation": "" + }, + { + "begin_time": 45901, + "end_time": 46105, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 46105, + "end_time": 46308, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 46308, + "end_time": 46512, + "text": "月", + "punctuation": "" + }, + { + "begin_time": 46512, + "end_time": 46716, + "text": "或", + "punctuation": "" + }, + { + "begin_time": 46716, + "end_time": 46920, + "text": "或", + "punctuation": "" + }, + { + "begin_time": 46920, + "end_time": 47124, + "text": "者", + "punctuation": "" + }, + { + "begin_time": 47124, + "end_time": 47328, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 47328, + "end_time": 47532, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 47532, + "end_time": 47736, + "text": "月", + "punctuation": "" + }, + { + "begin_time": 47736, + "end_time": 47940, + "text": "嘛", + "punctuation": "," + }, + { + "begin_time": 48420, + "end_time": 48697, + "text": "或", + "punctuation": "" + }, + { + "begin_time": 48697, + "end_time": 48975, + "text": "者", + "punctuation": "" + }, + { + "begin_time": 48975, + "end_time": 49252, + "text": "一", + "punctuation": "" + }, + { + "begin_time": 49252, + "end_time": 49530, + "text": "个", + "punctuation": "" + }, + { + "begin_time": 49530, + "end_time": 49807, + "text": "月", + "punctuation": "" + }, + { + "begin_time": 49807, + "end_time": 50085, + "text": "到", + "punctuation": "" + }, + { + "begin_time": 50085, + "end_time": 51195, + "text": "31号", + "punctuation": "、" + }, + { + "begin_time": 51195, + "end_time": 52305, + "text": "29号", + "punctuation": "," + }, + { + "begin_time": 52305, + "end_time": 52582, + "text": "你", + "punctuation": "" + }, + { + "begin_time": 52582, + "end_time": 52860, + "text": "看", + "punctuation": "" + }, + { + "begin_time": 52860, + "end_time": 53137, + "text": "这", + "punctuation": "" + }, + { + "begin_time": 53137, + "end_time": 53415, + "text": "就", + "punctuation": "" + }, + { + "begin_time": 53415, + "end_time": 53692, + "text": "可", + "punctuation": "" + }, + { + "begin_time": 53692, + "end_time": 53970, + "text": "以", + "punctuation": "" + }, + { + "begin_time": 53970, + "end_time": 54247, + "text": "看", + "punctuation": "。" + } + ] + }, + { + "begin_time": 54247, + "end_time": 55357, + "text": "出长春这。", + "sentence_id": 11, + "speaker_id": 0, + "words": [ + { + "begin_time": 54247, + "end_time": 54525, + "text": "出", + "punctuation": "" + }, + { + "begin_time": 54525, + "end_time": 54802, + "text": "长", + "punctuation": "" + }, + { + "begin_time": 54802, + "end_time": 55080, + "text": "春", + "punctuation": "" + }, + { + "begin_time": 55080, + "end_time": 55357, + "text": "这", + "punctuation": "。" + } + ] + }, + { + "begin_time": 55357, + "end_time": 57300, + "text": "两天没,不好意思。", + "sentence_id": 12, + "speaker_id": 1, + "words": [ + { + "begin_time": 55357, + "end_time": 55635, + "text": "两", + "punctuation": "" + }, + { + "begin_time": 55635, + "end_time": 55912, + "text": "天", + "punctuation": "" + }, + { + "begin_time": 55912, + "end_time": 56190, + "text": "没", + "punctuation": "," + }, + { + "begin_time": 56190, + "end_time": 56467, + "text": "不", + "punctuation": "" + }, + { + "begin_time": 56467, + "end_time": 56745, + "text": "好", + "punctuation": "" + }, + { + "begin_time": 56745, + "end_time": 57022, + "text": "意", + "punctuation": "" + }, + { + "begin_time": 57022, + "end_time": 57300, + "text": "思", + "punctuation": "。" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/uploads/test_1.xlsx b/uploads/test_1.xlsx new file mode 100644 index 0000000..b332b1c Binary files /dev/null and b/uploads/test_1.xlsx differ