Spaces:
Runtime error
Runtime error
Create requirements.txt
#30
by Banuka - opened
- AzureBlobStorageAudio.py +15 -26
- AzureBlobStorageVideo.py +21 -53
- Moviepy +106 -0
- Moviepy.py +0 -107
- README.md +0 -57
- apiTest.py +8 -0
- app.py +37 -133
- audio/1_seconds_haptic_audio.mp3 +0 -0
- audio/5_seconds_haptic_videos.mp3 +0 -0
- audio/5_seconds_vehicle_audio.mp3 +0 -0
- audio/8_seconds_Thunder.mp3 +0 -0
- audio/audioTrack.mp3 +0 -0
- mastering.py +0 -7
- masteringModule/.gitignore +81 -0
- mixing.py +7 -2
- video/audioTrack.mp3 +0 -0
AzureBlobStorageAudio.py
CHANGED
|
@@ -7,34 +7,23 @@ storage_account_key = "zhrGpPBX6PVD+krncC4nVF4yoweEku/z2ErVxjLiuu/CjAVKqM5O4xlGW
|
|
| 7 |
connection_string = f"DefaultEndpointsProtocol=https;AccountName={storage_account_name};AccountKey={storage_account_key};EndpointSuffix=core.windows.net"
|
| 8 |
|
| 9 |
container_name = "useruploadhuggingfaceaudio" # Update container name for audio files
|
| 10 |
-
|
| 11 |
-
|
| 12 |
|
| 13 |
|
| 14 |
-
def
|
| 15 |
-
|
| 16 |
-
Deletes all blobs within a specified Azure Blob Storage container.
|
| 17 |
-
|
| 18 |
-
Args:
|
| 19 |
-
container_id (str): The ID of the container to delete.
|
| 20 |
-
"""
|
| 21 |
-
try:
|
| 22 |
-
# Establish connection using your storage connection string (replace with yours)
|
| 23 |
-
storage_connection_string = 'DefaultEndpointsProtocol=https;AccountName=useruploadhuggingface;AccountKey=zhrGpPBX6PVD+krncC4nVF4yoweEku/z2ErVxjLiuu/CjAVKqM5O4xlGWEyuWGxptL3mA1pv/6P4+AStjSjLEQ==;EndpointSuffix=core.windows.net'
|
| 24 |
-
blob_service_client = azure.storage.blob.BlobServiceClient.from_connection_string(storage_connection_string)
|
| 25 |
-
|
| 26 |
-
# Get container client
|
| 27 |
-
container_client = blob_service_client.get_container_client(container_id)
|
| 28 |
-
|
| 29 |
-
# Delete all blobs in the container (iterator for large datasets)
|
| 30 |
-
blobs = container_client.list_blobs()
|
| 31 |
-
for blob in blobs:
|
| 32 |
-
container_client.delete_blob(blob.name)
|
| 33 |
-
print(f'Container "{container_id}" emptied successfully.')
|
| 34 |
-
|
| 35 |
-
except Exception as e:
|
| 36 |
-
print(f'Error deleting blobs: {e}')
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
def uploadUserAudioToBlobStorage(file_path, file_name):
|
| 40 |
"""Uploads an MP3 audio file to the specified Azure Blob Storage container and returns the URL.
|
|
@@ -88,4 +77,4 @@ if __name__ == "__main__":
|
|
| 88 |
container_client = blob_service_client.get_container_client(container_name)
|
| 89 |
|
| 90 |
# Pass container_client and file_name to the deletion function
|
| 91 |
-
|
|
|
|
| 7 |
connection_string = f"DefaultEndpointsProtocol=https;AccountName={storage_account_name};AccountKey={storage_account_key};EndpointSuffix=core.windows.net"
|
| 8 |
|
| 9 |
container_name = "useruploadhuggingfaceaudio" # Update container name for audio files
|
| 10 |
+
file_path = r"C:\Users\ASUS\Desktop\UoW\2ND YEAR\SDGP\AUDIO\edit\3_second_audio.flac" # Update path to your MP3 file
|
| 11 |
+
file_name = "uploaded_audio.mp3"
|
| 12 |
|
| 13 |
|
| 14 |
+
def deleteUserAudioFromBlobStorage(container_client,blob_name):
|
| 15 |
+
"""Deletes the specified blob from Azure Blob Storage.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
+
Args:
|
| 18 |
+
blob_client (BlobClient): The BlobClient object for the blob to delete.
|
| 19 |
+
"""
|
| 20 |
+
try:
|
| 21 |
+
# Get the blob client within the function for deletion
|
| 22 |
+
blob_client = container_client.get_blob_client(blob_name)
|
| 23 |
+
blob_client.delete_blob()
|
| 24 |
+
print(f"Audio deleted successfully from Azure Blob Storage.")
|
| 25 |
+
except Exception as e:
|
| 26 |
+
print(f"Error deleting audio: {e}")
|
| 27 |
|
| 28 |
def uploadUserAudioToBlobStorage(file_path, file_name):
|
| 29 |
"""Uploads an MP3 audio file to the specified Azure Blob Storage container and returns the URL.
|
|
|
|
| 77 |
container_client = blob_service_client.get_container_client(container_name)
|
| 78 |
|
| 79 |
# Pass container_client and file_name to the deletion function
|
| 80 |
+
deleteUserAudioFromBlobStorage(container_client, file_name)
|
AzureBlobStorageVideo.py
CHANGED
|
@@ -1,43 +1,28 @@
|
|
| 1 |
-
|
| 2 |
-
from azure.storage.blob import BlobServiceClient, generate_blob_sas, BlobSasPermissions
|
| 3 |
-
from datetime import datetime, timedelta
|
| 4 |
|
| 5 |
# Parameters for linking Azure to the application
|
| 6 |
storage_account_key = "zhrGpPBX6PVD+krncC4nVF4yoweEku/z2ErVxjLiuu/CjAVKqM5O4xlGWEyuWGxptL3mA1pv/6P4+AStjSjLEQ=="
|
| 7 |
storage_account_name = "useruploadhuggingface"
|
| 8 |
connection_string = f"DefaultEndpointsProtocol=https;AccountName={storage_account_name};AccountKey={storage_account_key};EndpointSuffix=core.windows.net"
|
| 9 |
container_name = "useruploadhuggingfacevideo"
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
target_container_id = 'useruploadhuggingfacevideo'
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
-
def
|
| 17 |
-
|
| 18 |
-
Deletes all blobs within a specified Azure Blob Storage container.
|
| 19 |
-
|
| 20 |
-
Args:
|
| 21 |
-
container_id (str): The ID of the container to delete.
|
| 22 |
-
"""
|
| 23 |
-
try:
|
| 24 |
-
# Establish connection using your storage connection string (replace with yours)
|
| 25 |
-
storage_connection_string = 'DefaultEndpointsProtocol=https;AccountName=useruploadhuggingface;AccountKey=zhrGpPBX6PVD+krncC4nVF4yoweEku/z2ErVxjLiuu/CjAVKqM5O4xlGWEyuWGxptL3mA1pv/6P4+AStjSjLEQ==;EndpointSuffix=core.windows.net'
|
| 26 |
-
blob_service_client = azure.storage.blob.BlobServiceClient.from_connection_string(storage_connection_string)
|
| 27 |
-
|
| 28 |
-
# Get container client
|
| 29 |
-
container_client = blob_service_client.get_container_client(container_id)
|
| 30 |
-
|
| 31 |
-
# Delete all blobs in the container (iterator for large datasets)
|
| 32 |
-
blobs = container_client.list_blobs()
|
| 33 |
-
for blob in blobs:
|
| 34 |
-
container_client.delete_blob(blob.name)
|
| 35 |
-
print(f'Container "{container_id}" emptied successfully.')
|
| 36 |
-
|
| 37 |
-
except Exception as e:
|
| 38 |
-
print(f'Error deleting blobs: {e}')
|
| 39 |
-
|
| 40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
|
| 43 |
def uploadUserVideoToBlobStorage(file_path, file_name):
|
|
@@ -79,27 +64,6 @@ def uploadUserVideoToBlobStorage(file_path, file_name):
|
|
| 79 |
print(f"Error: File not found at {file_path}.")
|
| 80 |
raise # Re-raise the exception for further handling
|
| 81 |
|
| 82 |
-
|
| 83 |
-
def generateSASToken(account_name,container_name, blob_name, account_key):
|
| 84 |
-
sas_token = generate_blob_sas(account_name=account_name,
|
| 85 |
-
container_name=container_name,
|
| 86 |
-
blob_name=blob_name,
|
| 87 |
-
account_key=account_key,
|
| 88 |
-
permission=BlobSasPermissions(read=True),
|
| 89 |
-
expiry=datetime.utcnow() + timedelta(hours=1))
|
| 90 |
-
|
| 91 |
-
print(f"SAS Token generated:{sas_token}")
|
| 92 |
-
|
| 93 |
-
return sas_token
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
def generateSASURL(account_name, container_name, blob_name, sas_token):
|
| 97 |
-
|
| 98 |
-
sas_url = 'https://' + account_name + '.blob.core.windows.net/' + container_name + '/' + blob_name + '?' + sas_token
|
| 99 |
-
print(f"SAS URL Generated: {sas_url}")
|
| 100 |
-
|
| 101 |
-
return sas_url
|
| 102 |
-
|
| 103 |
if __name__ == "__main__":
|
| 104 |
# Example usage
|
| 105 |
uploaded_video_url = uploadUserVideoToBlobStorage(file_path, file_name)
|
|
@@ -107,5 +71,9 @@ if __name__ == "__main__":
|
|
| 107 |
# Retrieve container_client from within the upload function
|
| 108 |
blob_service_client = BlobServiceClient.from_connection_string(conn_str=connection_string)
|
| 109 |
container_client = blob_service_client.get_container_client(container_name)
|
| 110 |
-
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from azure.storage.blob import BlobServiceClient
|
|
|
|
|
|
|
| 2 |
|
| 3 |
# Parameters for linking Azure to the application
|
| 4 |
storage_account_key = "zhrGpPBX6PVD+krncC4nVF4yoweEku/z2ErVxjLiuu/CjAVKqM5O4xlGWEyuWGxptL3mA1pv/6P4+AStjSjLEQ=="
|
| 5 |
storage_account_name = "useruploadhuggingface"
|
| 6 |
connection_string = f"DefaultEndpointsProtocol=https;AccountName={storage_account_name};AccountKey={storage_account_key};EndpointSuffix=core.windows.net"
|
| 7 |
container_name = "useruploadhuggingfacevideo"
|
| 8 |
+
file_path = r"C:\Users\ASUS\Desktop\UoW\2ND YEAR\SDGP\HuggingFace\Video\production_id_5091624 (1080p).mp4"
|
| 9 |
+
file_name = "uploaded_video.mp4"
|
|
|
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
+
def deleteUserVideoFromBlobStorage(container_client,blob_name):
|
| 14 |
+
"""Deletes the specified blob from Azure Blob Storage.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
Args:
|
| 17 |
+
blob_client (BlobClient): The BlobClient object for the blob to delete.
|
| 18 |
+
"""
|
| 19 |
+
try:
|
| 20 |
+
# Get the blob client within the function for deletion
|
| 21 |
+
blob_client = container_client.get_blob_client(blob_name)
|
| 22 |
+
blob_client.delete_blob()
|
| 23 |
+
print(f"Video deleted successfully from Azure Blob Storage.")
|
| 24 |
+
except Exception as e:
|
| 25 |
+
print(f"Error deleting video: {e}")
|
| 26 |
|
| 27 |
|
| 28 |
def uploadUserVideoToBlobStorage(file_path, file_name):
|
|
|
|
| 64 |
print(f"Error: File not found at {file_path}.")
|
| 65 |
raise # Re-raise the exception for further handling
|
| 66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
if __name__ == "__main__":
|
| 68 |
# Example usage
|
| 69 |
uploaded_video_url = uploadUserVideoToBlobStorage(file_path, file_name)
|
|
|
|
| 71 |
# Retrieve container_client from within the upload function
|
| 72 |
blob_service_client = BlobServiceClient.from_connection_string(conn_str=connection_string)
|
| 73 |
container_client = blob_service_client.get_container_client(container_name)
|
| 74 |
+
|
| 75 |
+
# Pass container_client and file_name to the deletion function
|
| 76 |
+
deleteUserVideoFromBlobStorage(container_client,file_name)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
|
Moviepy
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from moviepy.editor import *
|
| 2 |
+
import json
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
current_video_path = r"C:\Users\ASUS\Desktop\UoW\2ND YEAR\SDGP\Test Video\test_video.mp4"
|
| 6 |
+
current_video = VideoFileClip(current_video_path) # location of the uploaded video
|
| 7 |
+
output_query_response = '{"value":[{"documentId":"sp=r&st=2024-02-09T12:33:24Z&se=2025-08-06T20:33:24Z&spr=https&sv=2022-11-02&sr=b&sig=V%2Fq56JjGcL60r0vt3oAPjzx%2FZMu5%2BJo%2BfjKkJF2ccgo%3D","documentKind":"VideoInterval","start":"00:00:16","end":"00:00:26","best":"00:00:21","relevance":0.4005849361419678},{"documentId":"sp=r&st=2024-02-09T12:33:24Z&se=2025-08-06T20:33:24Z&spr=https&sv=2022-11-02&sr=b&sig=V%2Fq56JjGcL60r0vt3oAPjzx%2FZMu5%2BJo%2BfjKkJF2ccgo%3D","documentKind":"VideoInterval","start":"00:00:06","end":"00:00:16","best":"00:00:09","relevance":0.38852864503860474},{"documentId":"sp=r&st=2024-02-09T12:33:24Z&se=2025-08-06T20:33:24Z&spr=https&sv=2022-11-02&sr=b&sig=V%2Fq56JjGcL60r0vt3oAPjzx%2FZMu5%2BJo%2BfjKkJF2ccgo%3D","documentKind":"VideoInterval","start":"00:01:42","end":"00:01:58","best":"00:01:43","relevance":0.38718080520629883},{"documentId":"sp=r&st=2024-02-09T12:33:24Z&se=2025-08-06T20:33:24Z&spr=https&sv=2022-11-02&sr=b&sig=V%2Fq56JjGcL60r0vt3oAPjzx%2FZMu5%2BJo%2BfjKkJF2ccgo%3D","documentKind":"VideoInterval","start":"00:01:58","end":"00:02:14","best":"00:02:03","relevance":0.3811851143836975},{"documentId":"sp=r&st=2024-02-09T12:33:24Z&se=2025-08-06T20:33:24Z&spr=https&sv=2022-11-02&sr=b&sig=V%2Fq56JjGcL60r0vt3oAPjzx%2FZMu5%2BJo%2BfjKkJF2ccgo%3D","documentKind":"VideoInterval","start":"00:00:42","end":"00:00:52","best":"00:00:42","relevance":0.3765566647052765},{"documentId":"sp=r&st=2024-02-09T12:33:24Z&se=2025-08-06T20:33:24Z&spr=https&sv=2022-11-02&sr=b&sig=V%2Fq56JjGcL60r0vt3oAPjzx%2FZMu5%2BJo%2BfjKkJF2ccgo%3D","documentKind":"VideoInterval","start":"00:00:26","end":"00:00:42","best":"00:00:28","relevance":0.3718773126602173},{"documentId":"sp=r&st=2024-02-09T12:33:24Z&se=2025-08-06T20:33:24Z&spr=https&sv=2022-11-02&sr=b&sig=V%2Fq56JjGcL60r0vt3oAPjzx%2FZMu5%2BJo%2BfjKkJF2ccgo%3D","documentKind":"VideoInterval","start":"00:01:08","end":"00:01:24","best":"00:01:10","relevance":0.3707084357738495},{"documentId":"sp=r&st=2024-02-09T12:33:24Z&se=2025-08-06T20:33:24Z&spr=https&sv=2022-11-02&sr=b&sig=V%2Fq56JjGcL60r0vt3oAPjzx%2FZMu5%2BJo%2BfjKkJF2ccgo%3D","documentKind":"VideoInterval","start":"00:01:37","end":"00:01:42","best":"00:01:38","relevance":0.36235538125038147},{"documentId":"sp=r&st=2024-02-09T12:33:24Z&se=2025-08-06T20:33:24Z&spr=https&sv=2022-11-02&sr=b&sig=V%2Fq56JjGcL60r0vt3oAPjzx%2FZMu5%2BJo%2BfjKkJF2ccgo%3D","documentKind":"VideoInterval","start":"00:01:29","end":"00:01:37","best":"00:01:33","relevance":0.3606133460998535},{"documentId":"sp=r&st=2024-02-09T12:33:24Z&se=2025-08-06T20:33:24Z&spr=https&sv=2022-11-02&sr=b&sig=V%2Fq56JjGcL60r0vt3oAPjzx%2FZMu5%2BJo%2BfjKkJF2ccgo%3D","documentKind":"VideoInterval","start":"00:01:03","end":"00:01:08","best":"00:01:04","relevance":0.3513660728931427},{"documentId":"sp=r&st=2024-02-09T12:33:24Z&se=2025-08-06T20:33:24Z&spr=https&sv=2022-11-02&sr=b&sig=V%2Fq56JjGcL60r0vt3oAPjzx%2FZMu5%2BJo%2BfjKkJF2ccgo%3D","documentKind":"VideoInterval","start":"00:00:00","end":"00:00:06","best":"00:00:05","relevance":0.3378048241138458}]}' # JSON response
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
# Convert JSON string to Python dictionary
|
| 11 |
+
data = json.loads(output_query_response)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
# creating an empty array for the results
|
| 15 |
+
result = []
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
# iterate over every explosion occurrence and find start, end, best values using the ml algorithm
|
| 19 |
+
for i in data["value"]:
|
| 20 |
+
result.append([i["start"], i["end"], i["best"]])
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
# update start_explosion_time and end_explosion_time for each explosion occurrence
|
| 24 |
+
for explosion in result:
|
| 25 |
+
best_time = explosion[2]
|
| 26 |
+
best_time_seconds = sum(x * int(t) for x, t in zip([3600, 60, 1], best_time.split(":")))
|
| 27 |
+
start_explosion_time_seconds = best_time_seconds - 1
|
| 28 |
+
end_explosion_time_seconds = best_time_seconds + 2
|
| 29 |
+
explosion[0] = "{:02d}:{:02d}:{:02d}".format(start_explosion_time_seconds // 3600,
|
| 30 |
+
(start_explosion_time_seconds % 3600 // 60),
|
| 31 |
+
start_explosion_time_seconds % 60)
|
| 32 |
+
explosion[1] = "{:02d}:{:02d}:{:02d}".format(end_explosion_time_seconds // 3600,
|
| 33 |
+
(end_explosion_time_seconds % 3600 // 60),
|
| 34 |
+
end_explosion_time_seconds % 60)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
# Extracting audio from the video
|
| 38 |
+
current_audio_path = "current_audio.mp3"
|
| 39 |
+
if current_video.audio is not None:
|
| 40 |
+
current_video.audio.write_audiofile(current_audio_path) # location of the audio clip
|
| 41 |
+
print("Audio file has been extracted from the video")
|
| 42 |
+
else:
|
| 43 |
+
print("No audio found in the video.")
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
# Loading the whole audio clip from the uploaded video
|
| 47 |
+
current_audio = AudioFileClip(current_audio_path)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
# define the segments for the audio clips
|
| 51 |
+
final_audio_segments = []
|
| 52 |
+
haptic_audio_path = 'https://phonebrrdemonstration2.blob.core.windows.net/audio3secondsmp3/3_second_explosion_00001.mp3'
|
| 53 |
+
haptic_audio = AudioFileClip(haptic_audio_path)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
# Iterate through each explosion occurrence and create audio segments
|
| 57 |
+
for explosion in result:
|
| 58 |
+
start_explosion_time = sum(x * int(t) for x, t in zip([3600, 60, 1], explosion[0].split(":")))
|
| 59 |
+
end_explosion_time = sum(x * int(t) for x, t in zip([3600, 60, 1], explosion[1].split(":")))
|
| 60 |
+
|
| 61 |
+
beginning_audio_clip = current_audio.subclip(0, start_explosion_time)
|
| 62 |
+
end_audio_clip = current_audio.subclip(end_explosion_time)
|
| 63 |
+
|
| 64 |
+
# Adjust the duration of the haptic audio to match the duration between start and end explosion times
|
| 65 |
+
haptic_audio_duration = end_explosion_time - start_explosion_time
|
| 66 |
+
haptic_audio_clip = haptic_audio.subclip(0, haptic_audio_duration)
|
| 67 |
+
|
| 68 |
+
final_audio = concatenate_audioclips([beginning_audio_clip, haptic_audio_clip, end_audio_clip])
|
| 69 |
+
final_audio_segments.append(final_audio)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
# concatenate final audio segments
|
| 73 |
+
final_audio = concatenate_audioclips(final_audio_segments)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
# Match the audio duration with the video duration
|
| 77 |
+
final_audio = final_audio.set_duration(current_video.duration)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
# Save the enhanced audio
|
| 81 |
+
final_audio_path = "output.mp3"
|
| 82 |
+
final_audio.write_audiofile(final_audio_path)
|
| 83 |
+
print("Enhanced audio has been created")
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
# create a video without audio
|
| 87 |
+
extracted_video = current_video.without_audio()
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
# save the video without audio
|
| 91 |
+
extracted_video_path = "output.mp4"
|
| 92 |
+
extracted_video.write_videofile(extracted_video_path, fps=60)
|
| 93 |
+
print("Video without audio has been created")
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
# combine the final video without the enhanced audio
|
| 97 |
+
final_video = extracted_video.set_audio(final_audio)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
# save the final video
|
| 101 |
+
final_video_path = "final_video.mp4"
|
| 102 |
+
final_video.write_videofile(final_video_path)
|
| 103 |
+
print("Final video has been created")
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
|
Moviepy.py
DELETED
|
@@ -1,107 +0,0 @@
|
|
| 1 |
-
from moviepy.editor import *
|
| 2 |
-
import json
|
| 3 |
-
|
| 4 |
-
def load_json_output(output_query_response):
|
| 5 |
-
# Convert JSON string to Python dictionary
|
| 6 |
-
return json.loads(output_query_response)
|
| 7 |
-
|
| 8 |
-
def extract_audio_from_video(video_path):
|
| 9 |
-
video = VideoFileClip(video_path)
|
| 10 |
-
audio_path = "audio/current_audio.mp3"
|
| 11 |
-
if video.audio is not None:
|
| 12 |
-
video.audio.write_audiofile(audio_path)
|
| 13 |
-
print("Audio file has been extracted from the video")
|
| 14 |
-
return audio_path
|
| 15 |
-
else:
|
| 16 |
-
print("No audio found in the video.")
|
| 17 |
-
return None
|
| 18 |
-
|
| 19 |
-
def get_explosion_segments(json_data):
|
| 20 |
-
# creating an empty array for the results
|
| 21 |
-
result = []
|
| 22 |
-
# iterate over every explosion occurrence and find start, end, best values using the ml algorithm
|
| 23 |
-
for i in json_data["value"]:
|
| 24 |
-
result.append([i["start"], i["end"], i["best"]])
|
| 25 |
-
|
| 26 |
-
# update start_explosion_time and end_explosion_time for each explosion occurrence
|
| 27 |
-
for explosion in result:
|
| 28 |
-
best_time = explosion[2]
|
| 29 |
-
best_time_seconds = sum(x * int(t) for x, t in zip([3600, 60, 1], best_time.split(":")))
|
| 30 |
-
start_explosion_time_seconds = best_time_seconds - 2
|
| 31 |
-
end_explosion_time_seconds = best_time_seconds + 1
|
| 32 |
-
explosion[0] = "{:02d}:{:02d}:{:02d}".format(start_explosion_time_seconds // 3600,
|
| 33 |
-
(start_explosion_time_seconds % 3600 // 60),
|
| 34 |
-
start_explosion_time_seconds % 60)
|
| 35 |
-
explosion[1] = "{:02d}:{:02d}:{:02d}".format(end_explosion_time_seconds // 3600,
|
| 36 |
-
(end_explosion_time_seconds % 3600 // 60),
|
| 37 |
-
end_explosion_time_seconds % 60)
|
| 38 |
-
return result
|
| 39 |
-
|
| 40 |
-
def create_final_audio(current_audio_path, haptic_audio_path, explosion_segments):
|
| 41 |
-
current_audio = AudioFileClip(current_audio_path) # location of the uploaded video
|
| 42 |
-
# define the segments for the audio clips
|
| 43 |
-
final_audio_segments = []
|
| 44 |
-
#haptic_audio_path = haptic_audio_url
|
| 45 |
-
haptic_audio = AudioFileClip(haptic_audio_path)
|
| 46 |
-
|
| 47 |
-
# Iterate through each explosion occurrence and create audio segments
|
| 48 |
-
for explosion in explosion_segments:
|
| 49 |
-
best_explosion_time = explosion[2]
|
| 50 |
-
best_explosion_time_seconds = sum(x * int(t) for x, t in zip([3600, 60, 1], best_explosion_time.split(":")))
|
| 51 |
-
|
| 52 |
-
# Adjust the duration of the haptic audio to match the duration of the explosion
|
| 53 |
-
haptic_audio_duration = haptic_audio.duration
|
| 54 |
-
haptic_audio_clip = haptic_audio.subclip(0, haptic_audio_duration)
|
| 55 |
-
|
| 56 |
-
# Create an audio clip starting from the best explosion time
|
| 57 |
-
explosion_audio_clip = current_audio.subclip(best_explosion_time_seconds - 1,
|
| 58 |
-
best_explosion_time_seconds + haptic_audio_duration)
|
| 59 |
-
|
| 60 |
-
# Concatenate the haptic audio clip with the explosion audio clip
|
| 61 |
-
final_audio = concatenate_audioclips([explosion_audio_clip, haptic_audio_clip])
|
| 62 |
-
final_audio_segments.append(final_audio)
|
| 63 |
-
|
| 64 |
-
# concatenate final audio segments
|
| 65 |
-
final_audio = concatenate_audioclips(final_audio_segments)
|
| 66 |
-
# Match the audio duration with the video duration
|
| 67 |
-
final_audio = final_audio.set_duration(current_audio.duration)
|
| 68 |
-
return final_audio
|
| 69 |
-
|
| 70 |
-
def master_audio(audio_clip):
|
| 71 |
-
# Apply audio mastering techniques here
|
| 72 |
-
# Example: loudness normalization, equalization, compression, etc.
|
| 73 |
-
# Replace the following line with your audio mastering process
|
| 74 |
-
mastered_audio = audio_clip.fx(afx.audio_normalize)
|
| 75 |
-
return mastered_audio
|
| 76 |
-
|
| 77 |
-
def save_audio(audio_clip, file_path):
|
| 78 |
-
audio_clip.write_audiofile(file_path)
|
| 79 |
-
print("Enhanced audio has been created")
|
| 80 |
-
|
| 81 |
-
def without_audio(video_clip):
|
| 82 |
-
return video_clip.without_audio()
|
| 83 |
-
|
| 84 |
-
def combine_video_audio(video_clip, audio_clip):
|
| 85 |
-
return video_clip.set_audio(audio_clip)
|
| 86 |
-
|
| 87 |
-
def save_video(video_clip, file_path):
|
| 88 |
-
video_clip.write_videofile(file_path, fps=60)
|
| 89 |
-
print("Final video has been created")
|
| 90 |
-
|
| 91 |
-
def process_video(current_video_path, output_query_response):
|
| 92 |
-
json_data = load_json_output(output_query_response)
|
| 93 |
-
audio_path = extract_audio_from_video(current_video_path)
|
| 94 |
-
if audio_path:
|
| 95 |
-
explosion_segments = get_explosion_segments(json_data)
|
| 96 |
-
final_audio = create_final_audio(audio_path, explosion_segments)
|
| 97 |
-
final_audio_mastered = master_audio(final_audio)
|
| 98 |
-
save_audio(final_audio_mastered, "output.mp3")
|
| 99 |
-
|
| 100 |
-
current_video = VideoFileClip(current_video_path)# Extracting audio from the video
|
| 101 |
-
extracted_video = without_audio(current_video)
|
| 102 |
-
|
| 103 |
-
final_video = combine_video_audio(extracted_video, final_audio_mastered)
|
| 104 |
-
save_video(final_video, "final_video.mp4")
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -11,60 +11,3 @@ license: unknown
|
|
| 11 |
---
|
| 12 |
|
| 13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 14 |
-
|
| 15 |
-
# Phone Brr
|
| 16 |
-
|
| 17 |
-
This project empowers you to create engaging videos with haptic feedback seamlessly integrated. Leverage the power of Azure Video Analysis and Summary, MoviePy for video processing, and Gradio for an intuitive web user interface.
|
| 18 |
-
|
| 19 |
-
Link to the Hugging Face Space: https://huggingface.co/spaces/SE-09/HapticsProject
|
| 20 |
-
|
| 21 |
-
## Features
|
| 22 |
-
|
| 23 |
-
- **AI-powered Haptic Integration:** Intelligently identify video segments that benefit from haptic effects using Azure Video Analysis and Summary.
|
| 24 |
-
- **Effortless Video Editing:** Utilize MoviePy's capabilities for efficient video processing and precise editing.
|
| 25 |
-
- **Curated Haptic Library:** Store and manage haptic audio clips in Azure Blob Storage for flexible incorporation into your videos.
|
| 26 |
-
- **Web-based Interface:** Gradio provides a user-friendly web UI where you can effortlessly upload videos, select haptic audio clips, and generate the final enhanced video.
|
| 27 |
-
- **Audio Mastering:** Add a final touch of polish with AI-powered audio mastering to ensure a well-balanced and professional sound.
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
## Installation
|
| 31 |
-
|
| 32 |
-
**Prerequsites:** Ensure you have Python 3.8 or higher installed along with the required libraries:
|
| 33 |
-
- ` azure-cognitiveservices-videoanalyzer `
|
| 34 |
-
- ` moviepy `
|
| 35 |
-
- ` gradio `
|
| 36 |
-
- ` requests `
|
| 37 |
-
- ` pydub `
|
| 38 |
-
- ` ffmprg `
|
| 39 |
-
- ` subprocess `
|
| 40 |
-
- You can install them using
|
| 41 |
-
```bash
|
| 42 |
-
pip install azure-cognitiveservices-videoanalyzer moviepy gradio requests pydub ffmprg subprocess
|
| 43 |
-
```
|
| 44 |
-
|
| 45 |
-
## Usage
|
| 46 |
-
|
| 47 |
-
1. **Clone the Repository:**
|
| 48 |
-
- Use `git clone https://huggingface.co/spaces/SE-09/HapticsProject` to clone the Repository locally.
|
| 49 |
-
2. **Configuration:**
|
| 50 |
-
- Set up a gradio virtual environment following [these steps](https://www.gradio.app/guides/installing-gradio-in-a-virtual-environment) before installing gradio.
|
| 51 |
-
- Install the remianing python libraries and dependancies.
|
| 52 |
-
3. **Video Editing and Haptic Audio Integration:**
|
| 53 |
-
- Upload a Video file using the web interface.
|
| 54 |
-
- Additional optional Input fields to upload a custom Audio track along with specifying the instance to add haptics to.
|
| 55 |
-
- Click "Submit" button.
|
| 56 |
-
4. **Output:**
|
| 57 |
-
- Upon sucessful processing the output fields will preview a video that can be downloaded.
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
## Authors
|
| 62 |
-
|
| 63 |
-
- [@Hussain](https://github.com/HussainLatiff)
|
| 64 |
-
- [@Ravija](https://github.com/ravijaanthony)
|
| 65 |
-
- [@Shashika](https://github.com/Shashika-bit)
|
| 66 |
-
- [@Banuka](https://github.com/BanukaMandinu)
|
| 67 |
-
- [@Isuru](https://github.com/isururana)
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apiTest.py
CHANGED
|
@@ -136,9 +136,17 @@ sas_url_1= "https://store1video.blob.core.windows.net/haptic-vid/test_video.mp4?
|
|
| 136 |
#query of what you are looking for
|
| 137 |
instance_1 = "Explosion"
|
| 138 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
#Enter the sas token
|
| 140 |
sas_token_2 = "sp=r&st=2024-03-18T09:48:32Z&se=2026-07-02T17:48:32Z&spr=https&sv=2022-11-02&sr=b&sig=hLiFrDUtrutW9FWWRR7Z0Kbc4wkHs28YR9RXjVxw8uc%3D"
|
| 141 |
#the sas url
|
| 142 |
sas_url_2= "https://store1video.blob.core.windows.net/haptic-vid/desert_vehicle_test.mp4?sp=r&st=2024-03-18T09:48:32Z&se=2026-07-02T17:48:32Z&spr=https&sv=2022-11-02&sr=b&sig=hLiFrDUtrutW9FWWRR7Z0Kbc4wkHs28YR9RXjVxw8uc%3D"
|
| 143 |
#query of what you are looking for
|
| 144 |
instance_2 = "Vehicle racing"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
#query of what you are looking for
|
| 137 |
instance_1 = "Explosion"
|
| 138 |
|
| 139 |
+
#calling the method returns the nested list of the time frames
|
| 140 |
+
#test_explosion = videoAnalysis(sas_token_1, sas_url_1, instance_1)
|
| 141 |
+
#print(test_explosion)
|
| 142 |
+
|
| 143 |
#Enter the sas token
|
| 144 |
sas_token_2 = "sp=r&st=2024-03-18T09:48:32Z&se=2026-07-02T17:48:32Z&spr=https&sv=2022-11-02&sr=b&sig=hLiFrDUtrutW9FWWRR7Z0Kbc4wkHs28YR9RXjVxw8uc%3D"
|
| 145 |
#the sas url
|
| 146 |
sas_url_2= "https://store1video.blob.core.windows.net/haptic-vid/desert_vehicle_test.mp4?sp=r&st=2024-03-18T09:48:32Z&se=2026-07-02T17:48:32Z&spr=https&sv=2022-11-02&sr=b&sig=hLiFrDUtrutW9FWWRR7Z0Kbc4wkHs28YR9RXjVxw8uc%3D"
|
| 147 |
#query of what you are looking for
|
| 148 |
instance_2 = "Vehicle racing"
|
| 149 |
+
|
| 150 |
+
#calling the method returns the nested list of the time frames
|
| 151 |
+
#test_vehicle = videoAnalysis(sas_token_2, sas_url_2, instance_2)
|
| 152 |
+
#print(test_vehicle)
|
app.py
CHANGED
|
@@ -1,26 +1,11 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import os
|
| 3 |
-
import subprocess
|
| 4 |
-
# install moviepy dependency
|
| 5 |
-
moviepy = subprocess.run(["pip", "install", "moviepy"])
|
| 6 |
-
ffmpeg = subprocess.run(["pip", "install", "ffmpeg-python"])
|
| 7 |
-
pipUpdate = subprocess.run(["pip", "install", "--upgrade", "pip"])
|
| 8 |
from azure.storage.blob import BlobServiceClient
|
| 9 |
import AzureBlobStorageVideo
|
| 10 |
import AzureBlobStorageAudio
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
from Moviepy import get_explosion_segments
|
| 15 |
-
from Moviepy import create_final_audio
|
| 16 |
-
from Moviepy import save_audio
|
| 17 |
-
from Moviepy import without_audio
|
| 18 |
-
from Moviepy import combine_video_audio
|
| 19 |
-
from Moviepy import save_video
|
| 20 |
-
from moviepy.editor import *
|
| 21 |
-
import json
|
| 22 |
-
|
| 23 |
-
def predict_video(input_video, input_audio=None, input_choice="Explosions"):
|
| 24 |
global filename, file_size # Use the global keyword to refer to the global variables
|
| 25 |
|
| 26 |
# Check if the video is available
|
|
@@ -36,42 +21,10 @@ def predict_video(input_video, input_audio=None, input_choice="Explosions"):
|
|
| 36 |
|
| 37 |
if file_size > 20 * 1024 * 1024:
|
| 38 |
return [None, "Error: The upload exceeds file size 16MB. Please upload a smaller file."]
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
storage_account_key = "zhrGpPBX6PVD+krncC4nVF4yoweEku/z2ErVxjLiuu/CjAVKqM5O4xlGWEyuWGxptL3mA1pv/6P4+AStjSjLEQ=="
|
| 44 |
-
connection_string = f"DefaultEndpointsProtocol=https;AccountName={storage_account_name};AccountKey={storage_account_key};EndpointSuffix=core.windows.net"
|
| 45 |
-
|
| 46 |
-
video_container_name = "useruploadhuggingfacevideo"
|
| 47 |
-
audio_container_name = "useruploadhuggingfaceaudio"
|
| 48 |
-
|
| 49 |
-
# 1. Upload user video file to azure blob storage
|
| 50 |
-
|
| 51 |
-
videoBlobURL = AzureBlobStorageVideo.uploadUserVideoToBlobStorage(input_video, filename)
|
| 52 |
-
videoSASToken = AzureBlobStorageVideo.generateSASToken(storage_account_name,video_container_name, filename, storage_account_key)
|
| 53 |
-
videoSASURL = AzureBlobStorageVideo.generateSASURL(storage_account_name, video_container_name, filename, videoSASToken)
|
| 54 |
-
|
| 55 |
-
# 1.1. Upload user audio if available
|
| 56 |
-
|
| 57 |
-
userAudioInputFlag = False
|
| 58 |
-
|
| 59 |
-
if input_audio is not None:
|
| 60 |
-
userAudioInputFlag = True
|
| 61 |
-
else:
|
| 62 |
-
if (input_choice == "Explosions"):
|
| 63 |
-
input_audio = os.path.join(os.path.dirname(__file__), "audio/1_seconds_haptic_audio.mp3")
|
| 64 |
-
print("explosion selected")
|
| 65 |
-
elif (input_choice == "Lightning and Thunder"):
|
| 66 |
-
input_audio = os.path.join(os.path.dirname(__file__), "audio/8_seconds_Thunder.mp3")
|
| 67 |
-
print("lightning and thunder selected")
|
| 68 |
-
elif (input_choice == "Vehicle Racing"):
|
| 69 |
-
input_audio = os.path.join(os.path.dirname(__file__), "audio/5_seconds_vehicle_audio.mp3")
|
| 70 |
-
print("vehicle racing selected")
|
| 71 |
-
else:
|
| 72 |
-
input_audio = os.path.join(os.path.dirname(__file__), "audio/5_seconds_haptic_videos.mp3")
|
| 73 |
-
print("default selected")
|
| 74 |
-
|
| 75 |
"""
|
| 76 |
Processes the uploaded video (replace with your video analysis logic).
|
| 77 |
|
|
@@ -82,44 +35,16 @@ def predict_video(input_video, input_audio=None, input_choice="Explosions"):
|
|
| 82 |
Returns:
|
| 83 |
A list containing the processed video and a message string.
|
| 84 |
"""
|
| 85 |
-
|
|
|
|
| 86 |
|
| 87 |
-
#
|
| 88 |
-
if responseQueryText == """{"error":{"code":"InvalidRequest","message":"Value for indexName is invalid."}}""":
|
| 89 |
-
responseQueryText = videoAnalysis(videoSASURL, videoSASToken, input_choice)
|
| 90 |
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
-
json_data = load_json_output(responseQueryText)
|
| 94 |
-
|
| 95 |
-
# Extract audio from the video
|
| 96 |
-
audio_path = extract_audio_from_video(input_video)
|
| 97 |
-
# Get explosion segments
|
| 98 |
-
explosion_segments = get_explosion_segments(json_data)
|
| 99 |
-
|
| 100 |
-
print(input_audio)
|
| 101 |
-
|
| 102 |
-
# Create final audio
|
| 103 |
-
#final_audio = create_final_audio(audio_path, explosion_segments)
|
| 104 |
-
final_audio = create_final_audio(audio_path, input_audio, explosion_segments)
|
| 105 |
-
# Save enhanced audio
|
| 106 |
-
finalAudioPath = "audio/finalAudio.mp3"
|
| 107 |
-
save_audio(final_audio, finalAudioPath)
|
| 108 |
-
|
| 109 |
-
if (userAudioInputFlag == True):
|
| 110 |
-
AzureBlobStorageVideo.delete_container('useruploadhuggingfaceaudio')
|
| 111 |
-
|
| 112 |
-
# Extract video without audio
|
| 113 |
-
current_video = without_audio(VideoFileClip(input_video))
|
| 114 |
-
|
| 115 |
-
# Combine video with final audio
|
| 116 |
-
final_video = combine_video_audio(current_video, final_audio)
|
| 117 |
-
|
| 118 |
-
# Save final video
|
| 119 |
-
save_video(final_video, "video/final_enhanced_video.mp4")
|
| 120 |
-
finalVideoPath = "video/final_enhanced_video.mp4"
|
| 121 |
-
|
| 122 |
-
return [finalVideoPath, f"Video enhancement successful"]
|
| 123 |
|
| 124 |
css = """
|
| 125 |
#col-container {
|
|
@@ -127,9 +52,7 @@ css = """
|
|
| 127 |
max-width: 800px;
|
| 128 |
}
|
| 129 |
"""
|
| 130 |
-
|
| 131 |
-
audio_1 = os.path.join(os.path.dirname(__file__), "audio/audioTrack.mp3")
|
| 132 |
-
search_1 = "Explosions"
|
| 133 |
with gr.Blocks(css=css) as demo:
|
| 134 |
with gr.Column(elem_id="col-container"):
|
| 135 |
gr.HTML("""
|
|
@@ -137,57 +60,38 @@ with gr.Blocks(css=css) as demo:
|
|
| 137 |
<h3>Welcome to the Hugging Face Space of Phone brr! We aim to create more immersive content for mobile phones with the use of haptic audio, this demo focuses on working for a very commonly used special effect of explosions hope you enjoy it.</h3>
|
| 138 |
|
| 139 |
<p>Instructions:
|
| 140 |
-
<br>Step 1: Upload your
|
| 141 |
<br>Step 2: (Optional) Upload an MP3 audio track.
|
| 142 |
-
<br>Step 3:
|
| 143 |
-
<br>Step 4:
|
| 144 |
-
<br>Step 5:
|
| 145 |
-
<br>Step 6: View and download the final videoΒ withΒ haptics.
|
| 146 |
</p>
|
| 147 |
""")
|
| 148 |
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
allow_custom_value=True
|
| 159 |
-
)
|
| 160 |
-
with gr.Row():
|
| 161 |
-
btn_in = gr.Button("Submit", scale=0)
|
| 162 |
-
with gr.Column():
|
| 163 |
-
video_out = gr.Video(label="Output Video")
|
| 164 |
-
with gr.Row():
|
| 165 |
-
text_out = gr.Textbox(label="Output Text")
|
| 166 |
|
| 167 |
gr.Examples(
|
| 168 |
-
examples=[[
|
|
|
|
| 169 |
fn=predict_video,
|
| 170 |
-
inputs=[video_in, audio_in
|
| 171 |
outputs=[video_out, text_out],
|
| 172 |
-
|
| 173 |
)
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
<h3> Audio Library </h2>
|
| 177 |
-
<p> <a href="https://audiolibrary.blob.core.windows.net/audiolibrary/1_seconds_haptic_audio.mp3"> Explosion Audio Track 1 </a>
|
| 178 |
-
<br> <a href="https://audiolibrary.blob.core.windows.net/audiolibrary/5_seconds_haptic_videos.mp3"> Explosion Audio Track 2 </a>
|
| 179 |
-
<br> <a href="https://audiolibrary.blob.core.windows.net/audiolibrary/6_seconds_haptic_audio.mp3"> Explosion Audio Track 3 </a>
|
| 180 |
-
<br> <a href="https://audiolibrary.blob.core.windows.net/audiolibrary/7_seconds_haptic_audio.mp3"> Explosion Audio Track 4 </a>
|
| 181 |
-
<br> <a href="https://audiolibrary.blob.core.windows.net/audiolibrary/9_seconds_haptic_videos.mp3"> Explosion Audio Track 5 </a>
|
| 182 |
-
<br> <a href="https://audiolibrary.blob.core.windows.net/audiolibrary/5_seconds_vehicle_audio.mp3"> Vehicle Audio Track 1 </a>
|
| 183 |
-
<br> <a href="https://audiolibrary.blob.core.windows.net/audiolibrary/30_seconds_vehicle_audio.mp3"> Vehicle Audio Track 2 </a>
|
| 184 |
-
</p>
|
| 185 |
-
""")
|
| 186 |
-
|
| 187 |
-
btn_in.click(
|
| 188 |
fn=predict_video,
|
| 189 |
-
inputs=[video_in,audio_in
|
| 190 |
outputs=[video_out, text_out],
|
| 191 |
queue=False
|
| 192 |
-
|
|
|
|
| 193 |
demo.launch(debug=True)
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
from azure.storage.blob import BlobServiceClient
|
| 4 |
import AzureBlobStorageVideo
|
| 5 |
import AzureBlobStorageAudio
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def predict_video(input_video, input_audio=None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
global filename, file_size # Use the global keyword to refer to the global variables
|
| 10 |
|
| 11 |
# Check if the video is available
|
|
|
|
| 21 |
|
| 22 |
if file_size > 20 * 1024 * 1024:
|
| 23 |
return [None, "Error: The upload exceeds file size 16MB. Please upload a smaller file."]
|
| 24 |
+
|
| 25 |
+
#upload the video to AzureBlobStorage
|
| 26 |
+
AzureBlobStorageVideo.uploadUserVideoToBlobStorage(input_video,"test6")
|
| 27 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
"""
|
| 29 |
Processes the uploaded video (replace with your video analysis logic).
|
| 30 |
|
|
|
|
| 35 |
Returns:
|
| 36 |
A list containing the processed video and a message string.
|
| 37 |
"""
|
| 38 |
+
# Placeholder processing (replace with actual video analysis)
|
| 39 |
+
message = "**Placeholder:** Video processing not implemented yet."
|
| 40 |
|
| 41 |
+
# You can optionally add a progress bar or loading indicator here
|
|
|
|
|
|
|
| 42 |
|
| 43 |
+
if input_audio is None:
|
| 44 |
+
return [input_video, message + " Generated Audio will be used"]
|
| 45 |
+
AzureBlobStorageAudio.uploadUserAudioToBlobStorage(input_audio,"test8")
|
| 46 |
+
return [input_video, message + f" Using uploaded audio: {input_audio.name}"]
|
| 47 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
css = """
|
| 50 |
#col-container {
|
|
|
|
| 52 |
max-width: 800px;
|
| 53 |
}
|
| 54 |
"""
|
| 55 |
+
|
|
|
|
|
|
|
| 56 |
with gr.Blocks(css=css) as demo:
|
| 57 |
with gr.Column(elem_id="col-container"):
|
| 58 |
gr.HTML("""
|
|
|
|
| 60 |
<h3>Welcome to the Hugging Face Space of Phone brr! We aim to create more immersive content for mobile phones with the use of haptic audio, this demo focuses on working for a very commonly used special effect of explosions hope you enjoy it.</h3>
|
| 61 |
|
| 62 |
<p>Instructions:
|
| 63 |
+
<br>Step 1: Upload your video.
|
| 64 |
<br>Step 2: (Optional) Upload an MP3 audio track.
|
| 65 |
+
<br>Step 3: We'll analyze the video and suggest explosion timeframes using Azure Cognitive Services (not included yet).
|
| 66 |
+
<br>Step 4: Download haptic explosion audio from [link to audio source].
|
| 67 |
+
<br>Step 5: Mix the Audio using any app of your choice and master the audio with an AI mastering program (links provided).
|
|
|
|
| 68 |
</p>
|
| 69 |
""")
|
| 70 |
|
| 71 |
+
with gr.Row():
|
| 72 |
+
with gr.Column():
|
| 73 |
+
video_in = gr.File(label="Upload a Video", file_types=[".mp4"])
|
| 74 |
+
with gr.Column():
|
| 75 |
+
audio_in = gr.File(label="Optional: Upload an Audio Track", file_types=[".mp3"])
|
| 76 |
+
with gr.Column():
|
| 77 |
+
video_out = gr.Video(label="Output Video")
|
| 78 |
+
with gr.Row():
|
| 79 |
+
text_out = gr.Textbox(label="Output Text")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
gr.Examples(
|
| 82 |
+
examples=[[os.path.join(os.path.dirname(__file__), "video/test_video.mp4"),
|
| 83 |
+
os.path.join(os.path.dirname(__file__), "video/audioTrack.mp3")]],
|
| 84 |
fn=predict_video,
|
| 85 |
+
inputs=[video_in, audio_in],
|
| 86 |
outputs=[video_out, text_out],
|
| 87 |
+
cache_examples= False # Cache examples for faster loading
|
| 88 |
)
|
| 89 |
+
|
| 90 |
+
video_in.change(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
fn=predict_video,
|
| 92 |
+
inputs=[video_in, audio_in], # Use both video and audio inputs here
|
| 93 |
outputs=[video_out, text_out],
|
| 94 |
queue=False
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
demo.launch(debug=True)
|
audio/1_seconds_haptic_audio.mp3
DELETED
|
Binary file (70.1 kB)
|
|
|
audio/5_seconds_haptic_videos.mp3
DELETED
|
Binary file (204 kB)
|
|
|
audio/5_seconds_vehicle_audio.mp3
DELETED
|
Binary file (81.1 kB)
|
|
|
audio/8_seconds_Thunder.mp3
DELETED
|
Binary file (170 kB)
|
|
|
audio/audioTrack.mp3
DELETED
|
Binary file (81.1 kB)
|
|
|
mastering.py
DELETED
|
@@ -1,7 +0,0 @@
|
|
| 1 |
-
import subprocess
|
| 2 |
-
|
| 3 |
-
def masterAudio(inputPath,outputPath):
|
| 4 |
-
try:
|
| 5 |
-
master = subprocess.run(["node", "masteringModule/main.js", "--input", inputPath, "--output", outputPath])
|
| 6 |
-
except subprocess.CalledProcessError as err:
|
| 7 |
-
print("Error running Mastering Module: ", err)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
masteringModule/.gitignore
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Created by https://www.gitignore.io/api/node
|
| 2 |
+
|
| 3 |
+
### Node ###
|
| 4 |
+
# Logs
|
| 5 |
+
logs
|
| 6 |
+
*.log
|
| 7 |
+
npm-debug.log*
|
| 8 |
+
yarn-debug.log*
|
| 9 |
+
yarn-error.log*
|
| 10 |
+
|
| 11 |
+
# Runtime data
|
| 12 |
+
pids
|
| 13 |
+
*.pid
|
| 14 |
+
*.seed
|
| 15 |
+
*.pid.lock
|
| 16 |
+
|
| 17 |
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
| 18 |
+
lib-cov
|
| 19 |
+
|
| 20 |
+
# Coverage directory used by tools like istanbul
|
| 21 |
+
coverage
|
| 22 |
+
|
| 23 |
+
# nyc test coverage
|
| 24 |
+
.nyc_output
|
| 25 |
+
|
| 26 |
+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
| 27 |
+
.grunt
|
| 28 |
+
|
| 29 |
+
# Bower dependency directory (https://bower.io/)
|
| 30 |
+
bower_components
|
| 31 |
+
|
| 32 |
+
# node-waf configuration
|
| 33 |
+
.lock-wscript
|
| 34 |
+
|
| 35 |
+
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
| 36 |
+
build/Release
|
| 37 |
+
|
| 38 |
+
# Dependency directories
|
| 39 |
+
node_modules/
|
| 40 |
+
jspm_packages/
|
| 41 |
+
|
| 42 |
+
# TypeScript v1 declaration files
|
| 43 |
+
typings/
|
| 44 |
+
|
| 45 |
+
# Optional npm cache directory
|
| 46 |
+
.npm
|
| 47 |
+
|
| 48 |
+
# Optional eslint cache
|
| 49 |
+
.eslintcache
|
| 50 |
+
|
| 51 |
+
# Optional REPL history
|
| 52 |
+
.node_repl_history
|
| 53 |
+
|
| 54 |
+
# Output of 'npm pack'
|
| 55 |
+
*.tgz
|
| 56 |
+
|
| 57 |
+
# Yarn Integrity file
|
| 58 |
+
.yarn-integrity
|
| 59 |
+
|
| 60 |
+
# dotenv environment variables file
|
| 61 |
+
.env
|
| 62 |
+
|
| 63 |
+
# parcel-bundler cache (https://parceljs.org/)
|
| 64 |
+
.cache
|
| 65 |
+
|
| 66 |
+
# next.js build output
|
| 67 |
+
.next
|
| 68 |
+
|
| 69 |
+
# nuxt.js build output
|
| 70 |
+
.nuxt
|
| 71 |
+
|
| 72 |
+
# vuepress build output
|
| 73 |
+
.vuepress/dist
|
| 74 |
+
|
| 75 |
+
# Serverless directories
|
| 76 |
+
.serverless
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
# End of https://www.gitignore.io/api/node
|
| 80 |
+
|
| 81 |
+
/output.wav
|
mixing.py
CHANGED
|
@@ -20,7 +20,6 @@ def mixingAudio(sound1_path, sound2_path,position):
|
|
| 20 |
sound1_path = check_and_convert(sound1_path)
|
| 21 |
sound2_path = check_and_convert(sound2_path)
|
| 22 |
|
| 23 |
-
# Load audio files
|
| 24 |
sound1 = AudioSegment.from_file(sound1_path)
|
| 25 |
sound2 = AudioSegment.from_file(sound2_path)
|
| 26 |
|
|
@@ -29,4 +28,10 @@ def mixingAudio(sound1_path, sound2_path,position):
|
|
| 29 |
|
| 30 |
# Save the result (assuming mp3 format)
|
| 31 |
output.export(r"mixedAudio\mixed_haptic_audioFile.mp3", format="mp3")
|
| 32 |
-
return "The audio
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
sound1_path = check_and_convert(sound1_path)
|
| 21 |
sound2_path = check_and_convert(sound2_path)
|
| 22 |
|
|
|
|
| 23 |
sound1 = AudioSegment.from_file(sound1_path)
|
| 24 |
sound2 = AudioSegment.from_file(sound2_path)
|
| 25 |
|
|
|
|
| 28 |
|
| 29 |
# Save the result (assuming mp3 format)
|
| 30 |
output.export(r"mixedAudio\mixed_haptic_audioFile.mp3", format="mp3")
|
| 31 |
+
return "The audio have been mixed."
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# for testing
|
| 35 |
+
# sound1_path = r"audio\mixkit-distant-explosion-1690.wav"
|
| 36 |
+
# sound2_path = r"audio\videoplayback_mastered (1).wav"
|
| 37 |
+
# mixingAudio(sound1_path, sound2_path,1000)
|
video/audioTrack.mp3
ADDED
|
Binary file (496 kB). View file
|
|
|