Skip to content

Instantly share code, notes, and snippets.

@ggomagundan
Last active May 12, 2020 05:01
Show Gist options
  • Save ggomagundan/c178f103ff76a3c3002735647fb6a6bb to your computer and use it in GitHub Desktop.
Save ggomagundan/c178f103ff76a3c3002735647fb6a6bb to your computer and use it in GitHub Desktop.
Finish Topic
[package]
name = "move_origin"
version = "0.1.0"
authors = ["Kai <ggogun@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
aws_lambda_events = "^0.3.0"
rusoto_core = "0.42.0"
rusoto_s3 = "0.42.0"
rusoto_credential = "0.42.0"
lambda_runtime = "^0.2.1"
serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0"
serde_derive = "^1"
log = "^0.4"
simple_logger = "^1"
openssl = { version = "0.10", features = ["vendored"] }
dotenv = "0.14.1"
dotenv_codegen = "0.14.1"
chrono = { version = "0.4" , features = ["serde"] }
regex = "1"
reqwest = { version = "0.10", features = ["json", "blocking"] }
[[bin]]
name = "bootstrap"
path = "src/main.rs"
#[macro_use]
extern crate dotenv_codegen;
use lambda_runtime::{error::HandlerError, lambda};
use std::error::Error;
use serde::{Serialize, Deserialize};
use rusoto_core::region::Region;
use rusoto_s3::{S3, S3Client, CopyObjectRequest};
use regex::Regex;
use aws_lambda_events::event::sns::SnsEvent;
mod models;
use models::mediaconvert_response::*;
use models::video::*;
use models::video_sources::*;
// This is our function entry point.
// Note the use of the `lambda!` macro. It will hold our handler:
// pub type Handler<E, O> = fn(E, Context) -> Result<O, HandlerError>
fn main() -> Result<(), Box<dyn Error>> {
// lambda!(my_handler);
// Ok(())
let j = "{\"version\":\"0\",\"id\":\"4914e037-36b4-27e9-1a0e-e2d377e74b13\",\"detail-type\":\"MediaConvert Job State Change\",\"source\":\"aws.mediaconvert\",\"account\":\"578297789655\",\"time\":\"2020-03-13T09:03:34Z\",\"region\":\"ap-northeast-2\",\"resources\":[\"arn:aws:mediaconvert:ap-northeast-2:578297789655:jobs/1584090167837-pl4uo5\"],\"detail\":{\"timestamp\":1584090214483,\"accountId\":\"578297789655\",\"queue\":\"arn:aws:mediaconvert:ap-northeast-2:578297789655:queues/Default\",\"jobId\":\"1584090167837-pl4uo5\",\"status\":\"COMPLETE\",\"userMetadata\":{\"text_content\": \"#샌드바 #카네오헤 #천국의바다 #테스트#강 #리붜 #rriivveerr 강은 영어로 뤼붜\",\"user_id\": \"204364\",\"original_name\": \"down-park-won.mp4\"},\"outputGroupDetails\":[{\"outputDetails\":[{\"outputFilePaths\":[\"s3://btoz-video-output/20200313/down-park-won/HLS_480p/down-park-won20200313T090252_output_480p.m3u8\"],\"durationInMs\":242400,\"videoDetails\":{\"widthInPx\":480,\"heightInPx\":640}}],\"playlistFilePaths\":[\"s3://btoz-video-output/20200313/down-park-won/HLS_480p/down-park-won.m3u8\"],\"type\":\"HLS_GROUP\"},{\"outputDetails\":[{\"outputFilePaths\":[\"s3://btoz-video-output/20200313/down-park-won/HLS_240p/down-park-won20200313T090252_output_240p.m3u8\"],\"durationInMs\":242400,\"videoDetails\":{\"widthInPx\":240,\"heightInPx\":352}}],\"playlistFilePaths\":[\"s3://btoz-video-output/20200313/down-park-won/HLS_240p/down-park-won.m3u8\"],\"type\":\"HLS_GROUP\"},{\"outputDetails\":[{\"outputFilePaths\":[\"s3://btoz-video-output/20200313/down-park-won/HLS_720p/down-park-won20200313T090252_output_720p.m3u8\"],\"durationInMs\":242400,\"videoDetails\":{\"widthInPx\":720,\"heightInPx\":1280}}],\"playlistFilePaths\":[\"s3://btoz-video-output/20200313/down-park-won/HLS_720p/down-park-won.m3u8\"],\"type\":\"HLS_GROUP\"},{\"outputDetails\":[{\"outputFilePaths\":[\"s3://btoz-video-output/20200313/down-park-won/thumbnails/down-park-won20200313T090251_output_thumbnail.0000005.jpg\"],\"durationInMs\":24000,\"videoDetails\":{\"widthInPx\":720,\"heightInPx\":1280}}],\"type\":\"FILE_GROUP\"}]}}";
let u: MediaConvertResponse = serde_json::from_str(j).unwrap();
println!("{:#?}", u);
match my_handler(u) {
Ok(_r) => Ok(()),
Err(e) => panic!("There was a problem opening the file: {:?}", e)
}
}
#[derive(Serialize, Deserialize)]
pub struct CustomOutput {
status_code: i64,
body: String
}
// Our handler will just check for a query string parameter called `firstName`.
// Note the different behavior depending on the value of the parameter.
// In case there's no query string parameter, we'll check the body of the request.
// The body comes as a string so we'll have to use `Serde` again to deserialize it.
// Finally, if we have no body, we'll return a default response.
fn my_handler(e: MediaConvertResponse)
// fn my_handler(e: SnsEvent, c: lambda_runtime::Context)
-> Result<CustomOutput, HandlerError> {
// $ cargo build --release --target x86_64-unknown-linux-musl
// $ zip -j rust.zip ./target/x86_64-unknown-linux-musl/release/bootstrap
let s3_client = S3Client::new(Region::ApNortheast2);
let from_bucket = dotenv!("UPLOAD_BUCKET_NAME").to_owned();
let to_bucket = dotenv!("COPY_BUCKET_NAME").to_owned();
let mut url_list: Vec<String> = Vec::new();
// let media_convert_detail = e.records[0].sns.message.as_ref().unwrap();
// println!("detail : {:?}", media_convert_detail);
// let detail_contents: MediaConvertResponse = serde_json::from_str(media_convert_detail).unwrap();
let detail_contents = e.clone();
println!("detail json: {:#?}", detail_contents);
//
let output_group_details = detail_contents.clone().detail.clone().unwrap().output_group_details.unwrap();
let metadata = detail_contents.clone().detail.unwrap().user_metadata.unwrap();
let mut video_sources: Vec<VideoSource> = Vec::new();
let mut s3_thumbnail_paths: &str = "";
let mut thumbnail_url: String = String::new() ;
for a in output_group_details.iter()
{
let width = &a.output_details.as_ref().unwrap()[0].video_details.as_ref().unwrap().width_in_px;
match &a.playlist_file_paths {
Some(paths) => {
let detail = &paths[0];
let re = Regex::new(r"s3://\w+.+.m3u8").unwrap();
if re.is_match(&detail) {
let mat = re.find(&detail).unwrap();
let url: String = mat.as_str().to_string().clone();
let bucket_path: Vec<&str> = url.split(&to_bucket).collect();
video_sources.push(VideoSource{
format: "hls" ,
s3_url: Some(url.clone()),
url: make_s3_public_url(bucket_path[1]),
width: *width
})
}
}
None => {
s3_thumbnail_paths = &a.output_details.as_ref().unwrap()[0].output_file_paths.as_ref().unwrap()[0];
let s3_thumbnail_path: Vec<&str> = s3_thumbnail_paths.split(&to_bucket).collect();
thumbnail_url = make_s3_public_url(s3_thumbnail_path[1]);
}
}
};
let uurl = video_sources[0].clone().s3_url.unwrap();
let splited: Vec<&str> = uurl.split("btoz-video-output/").collect();
let copy_to_paths: Vec<&str> = splited[1].split("/").collect();
let copy_to_path: &str = copy_to_paths[0];
println!("Move to Origin file");
let source_file = metadata.get("original_name").unwrap();
println!("from: {:?}, to {:}, sourcefile; {:}", from_bucket, to_bucket, source_file);
println!("source full : {:}", format!("{:}/{:}",from_bucket,source_file.clone()));
// let result = s3_client.copy_object(CopyObjectRequest {
// bucket: to_bucket ,
// copy_source: format!("{:}/{:}", from_bucket, source_file.clone()),
// key: format!("{:}/{:}",copy_to_path,source_file.clone()),
// ..Default::default()
// }).sync();
// println!("Result: {:?}", result);
let user_id: u64 = metadata.get("user_id").unwrap().parse().unwrap();
let video: Video = Video {
name: source_file,
thumbnail: thumbnail_url.as_str().clone(),
text_content: metadata.get("text_content").unwrap(),
sources: video_sources,
user_id: user_id
};
println!("request Body: {:?}", serde_json::to_string(&video).unwrap());
// let client = reqwest::blocking::Client::new();
// let request_url = dotenv!("REQUEST_URL");
// let request_auth_key = dotenv!("REQUEST_AUTH_KEY");
// let resp = client.post(request_url)
// .header("Content-Type","application/json")
// .header("Authorization",format!("Bearer {}", request_auth_key))
// .body(serde_json::to_string(&video).unwrap())
// .send();
// println!("{:#?}", resp);
Ok(CustomOutput {
status_code: 200,
body: format!("Upload job ({:?}) finished ", "123")//c.aws_request_id)
})
}
fn make_s3_public_url(detail_path: &str) -> String {
let bucket_name = dotenv!("COPY_BUCKET_NAME").to_owned();
format!("https://{}.s3.{}.amazonaws.com{}",
&bucket_name,
&Region::ApNortheast2.name(),
detail_path)
}
use chrono::Utc;
use std::collections::HashMap;
// use chrono::prelude::*;
use serde::{Deserialize, Serialize};
// #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
// pub struct SnsEvents {
// #[serde(rename = "Records")]
// pub records: Vec<SnsEvent>,
// }
//
// #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
// pub struct SnsEvent {
// #[serde(rename = "EventSource")]
// pub event_source: Option<String>,
// #[serde(rename = "EventVersion")]
// pub event_version: Option<String>,
// #[serde(rename = "EventSubscriptionArn")]
// pub event_subscription_arn: Option<String>,
// #[serde(rename = "Sns")]
// pub sns: Sns
// }
//
// #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
// pub struct Sns {
// pub Type: Option<String>,
// pub MessageId: Option<String>,
// pub TopicArn: Option<String>,
// pub Subject: Option<String>,
// #[serde(rename = "Message")]
// pub message: MediaConvertResponse,
// pub Timestamp: Option<String>,
// pub SignatureVersion: Option<String>,
// pub Signature: Option<String>,
// pub SigningCertUrl: Option<String>,
// pub UnsubscribeUrl: Option<String>,
// //MessageAttributes":{"key":{"Type":"String","Value":"value"}}
// }
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct MediaConvertResponse {
pub version: Option<String>,
pub id: Option<String>,
#[serde(rename = "detail-type")]
pub detail_type: Option<String>,
pub detail: Option<Detail>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Detail {
// timestamp: Option<Utc>,
#[serde(rename = "accountId")]
pub account_id: Option<String>,
pub queue: Option<String>,
#[serde(rename = "jobId")]
pub job_id: Option<String>,
pub status: Option<String>,
#[serde(rename = "outputGroupDetails")]
pub output_group_details: Option<Vec<OutputGroupDetails>>,
#[serde(rename = "userMetadata")]
pub user_metadata: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct OutputGroupDetails {
#[serde(rename = "outputDetails")]
pub output_details: Option<Vec<OutputDetails>>,
#[serde(rename = "playlistFilePaths")]
pub playlist_file_paths: Option<Vec<String>>,
pub r#type: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct OutputDetails {
#[serde(rename = "outputFilePaths")]
pub output_file_paths: Option<Vec<String>>,
#[serde(rename = "durationInMs")]
pub duration_in_ms: Option<u64>,
#[serde(rename = "videoDetails")]
pub video_details: Option<VideoDetail>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct VideoDetail {
#[serde(rename = "widthInPx")]
pub width_in_px: u64,
#[serde(rename = "heightInPx")]
pub height_in_px: u64,
}
pub mod video;
pub mod video_sources;
use crate::models::video_sources::VideoSource;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Video<'a> {
pub name: &'a str,
pub thumbnail: &'a str,
pub text_content: &'a str ,
pub sources: Vec<VideoSource<'a>>,
pub user_id: u64
}
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct VideoSource<'a> {
pub format: &'a str,
pub url: String,
pub s3_url: Option<String>,
pub width: u64
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment