Merge branch 'ref' into 'master'

fix video size

See merge request alzheimer-gaming/publishhelperbot!4
This commit is contained in:
Kirill Poletaev 2023-02-27 19:39:41 +00:00
commit ce1ff5f94b
6 changed files with 107 additions and 42 deletions

View File

@ -2,7 +2,7 @@ module PublishHelperBot.Environment
open System open System
open System.IO open System.IO
open Newtonsoft.Json open System.Text.Json
open Serilog open Serilog
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
@ -20,7 +20,7 @@ type public BotConfig = {
} }
let private readConfig = let private readConfig =
File.ReadAllText >> JsonConvert.DeserializeObject<BotConfig> File.ReadAllText >> JsonSerializer.Deserialize<BotConfig>
let public createConfig (name: string) = let public createConfig (name: string) =
match Environment.GetEnvironmentVariable(name) with match Environment.GetEnvironmentVariable(name) with

View File

@ -7,6 +7,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="System.Text.Json" Version="7.0.2" />
<PackageReference Include="Telegram.Bot" Version="18.0.0" /> <PackageReference Include="Telegram.Bot" Version="18.0.0" />
</ItemGroup> </ItemGroup>

View File

@ -123,7 +123,7 @@ module BotUpdateType =
module TgService = module TgService =
type private Msg = type private Msg =
| Ping | Ping
| PostVideo of url: string * savePath: string * externalId: Guid | PostVideo of PostVideoArgs
| PostRelay of RelayArgs | PostRelay of RelayArgs
| PostMessageToAdminChat of text: string | PostMessageToAdminChat of text: string
@ -198,32 +198,55 @@ module TgService =
return! loop () return! loop ()
| PostVideo (url, savePath, externalId) -> | PostVideo args ->
try try
Logging.logger.Information("Reading file path = {path}", savePath) Logging.logger.Information("PostVideo args = {args}", args)
use file = File.OpenRead(savePath) Logging.logger.Information("Reading file path = {path}", args.SavePath)
use file = File.OpenRead(args.SavePath)
if (file.Length / 1024L / 1024L) < 50L then if (file.Length / 1024L / 1024L) < 50L then
let input = InputOnlineFile(file, Path.GetRandomFileName()) let input = InputOnlineFile(file, Path.GetRandomFileName())
let caption = $"Source: {url}" let caption = $"Source: {args.Url}"
Logging.logger.Information( Logging.logger.Information(
"Sending video to channel, channelId = {channelId}, caption = {caption}", "Sending video to channel, channelId = {channelId}, caption = {caption}",
config.ChannelId, config.ChannelId,
caption) caption)
do! config.Client.SendVideoAsync(
let dimensions =
args.Width
|> Option.bind (fun w ->
args.Height
|> Option.map (fun h -> (w, h))
)
let sendVideo =
match dimensions with
| Some (width, height) ->
config.Client.SendVideoAsync(
config.ChannelId,
input,
caption = caption,
width = width,
height = height
)
| None ->
config.Client.SendVideoAsync(
config.ChannelId, config.ChannelId,
input, input,
caption = caption caption = caption
) )
do! sendVideo
|> Async.AwaitTask |> Async.AwaitTask
|> Async.Catch |> Async.Catch
|> Async.Ignore |> Async.Ignore
else else
inbox.Post(PostMessageToAdminChat($"Да блять, видео вышло больше 50мб: {externalId}")) inbox.Post(PostMessageToAdminChat($"Да блять, видео вышло больше 50мб: {args.ExternalId}"))
finally finally
Logging.logger.Information("Deleting file path = {path}", savePath) Logging.logger.Information("Deleting file path = {path}", args.SavePath)
File.Delete(savePath) File.Delete(args.SavePath)
match! config.YoutubeDlClient.CleanJob(externalId) with match! config.YoutubeDlClient.CleanJob(args.ExternalId) with
| Ok _ -> () | Ok _ -> ()
| Error _ -> () | Error _ -> ()
return! loop () return! loop ()
@ -243,8 +266,8 @@ module TgService =
member this.PostMessageToAdminChat(text) = member this.PostMessageToAdminChat(text) =
inbox.Post(PostMessageToAdminChat(text)) inbox.Post(PostMessageToAdminChat(text))
member this.PostVideo(url, savePath, externalId) = member this.PostVideo(args) =
inbox.Post(PostVideo(url, savePath, externalId)) } inbox.Post(PostVideo(args)) }
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module TgUpdateHandler = module TgUpdateHandler =

View File

@ -1,26 +1,48 @@
module PublishHelperBot.Types module PublishHelperBot.Types
open System open System
open System.Text.Json.Serialization
open Telegram.Bot open Telegram.Bot
open Telegram.Bot.Types open Telegram.Bot.Types
type ConfigChatId = int64 type ConfigChatId = int64
type CreateYoutubeDlJob = { type CreateYoutubeDlJob = {
url: string [<JsonPropertyName("url")>]
savePath: string Url: string
[<JsonPropertyName("savePath")>]
SavePath: string
} }
type CreateYoutubeDlJobSuccess = { type CreateYoutubeDlJobSuccess = {
task: Guid [<JsonPropertyName("task")>]
Task: Guid
}
type YoutubeDlInfoDict = {
[<JsonPropertyName("title")>]
Title: string option
[<JsonPropertyName("height")>]
Height: int option
[<JsonPropertyName("width")>]
Width: int option
}
type YoutubeDlStateInfo = {
[<JsonPropertyName("info_dict")>]
InfoDict: YoutubeDlInfoDict option
} }
type YoutubeDlStateResponse = { type YoutubeDlStateResponse = {
state: string [<JsonPropertyName("state")>]
State: string
[<JsonPropertyName("_youtube-dl_")>]
VideoInfo: YoutubeDlStateInfo option
} }
type YoutubeDlError = { type YoutubeDlError = {
message: string [<JsonPropertyName("message")>]
Message: string
} }
type CreateJobResult = Result<CreateYoutubeDlJobSuccess, YoutubeDlError> type CreateJobResult = Result<CreateYoutubeDlJobSuccess, YoutubeDlError>
@ -70,8 +92,17 @@ type BotUpdateType =
type ITgUpdateHandler = type ITgUpdateHandler =
abstract member PostUpdate: Update -> unit abstract member PostUpdate: Update -> unit
type PostVideoArgs = {
Url: string
SavePath: string
ExternalId: Guid
Title: string option
Width: int option
Height: int option
}
type ITgService = type ITgService =
abstract member PostRelay: args: RelayArgs -> unit abstract member PostRelay: args: RelayArgs -> unit
abstract member PostMessageToAdminChat: text: string -> unit abstract member PostMessageToAdminChat: text: string -> unit
abstract member Ping: unit -> unit abstract member Ping: unit -> unit
abstract member PostVideo: url: string * savePath: string * externalId: Guid -> unit abstract member PostVideo: PostVideoArgs -> unit

View File

@ -5,9 +5,9 @@ open System.Collections.Generic
open System.IO open System.IO
open System.Net.Http open System.Net.Http
open System.Text open System.Text
open System.Text.Json
open System.Threading.Tasks open System.Threading.Tasks
open Microsoft.FSharp.Core open Microsoft.FSharp.Core
open Newtonsoft.Json
open PublishHelperBot.Environment open PublishHelperBot.Environment
open PublishHelperBot.Types open PublishHelperBot.Types
@ -57,13 +57,13 @@ module YoutubeDlClient =
match res.IsSuccessStatusCode with match res.IsSuccessStatusCode with
| true -> | true ->
Logging.logger.Information("Response OK") Logging.logger.Information("Response OK")
Ok (JsonConvert.DeserializeObject<'TRes>(content)) Ok (JsonSerializer.Deserialize<'TRes>(content))
| false -> | false ->
Logging.logger.Error("Response network error") Logging.logger.Error("Response network error")
Error { message = "Unknown network error" } Error { Message = "Unknown network error" }
with ex -> with ex ->
Logging.logger.Error(ex, "Youtube api error") Logging.logger.Error(ex, "Youtube api error")
return Error { message = ex.Message } return Error { Message = ex.Message }
} |> Async.AwaitTask } |> Async.AwaitTask
let private createInbox(config: YoutubeDlClientConfig) = MailboxProcessor.Start(fun inbox -> let private createInbox(config: YoutubeDlClientConfig) = MailboxProcessor.Start(fun inbox ->
@ -74,7 +74,7 @@ module YoutubeDlClient =
Logging.logger.Information("Received youtube api create job") Logging.logger.Information("Received youtube api create job")
async { async {
try try
let json = args |> JsonConvert.SerializeObject let json = args |> JsonSerializer.Serialize
Logging.logger.Information("Sending create job = {Job}", json) Logging.logger.Information("Sending create job = {Job}", json)
use content = new StringContent(json, Encoding.UTF8, "application/json") use content = new StringContent(json, Encoding.UTF8, "application/json")
let! result = let! result =
@ -86,7 +86,7 @@ module YoutubeDlClient =
with ex -> with ex ->
Logging.logger.Error(ex, "Failed to create youtube api job") Logging.logger.Error(ex, "Failed to create youtube api job")
tcs.SetResult(Error { tcs.SetResult(Error {
message = ex.Message Message = ex.Message
}) })
} |> Async.Start } |> Async.Start
@ -110,7 +110,7 @@ module YoutubeDlClient =
with ex -> with ex ->
Logging.logger.Error(ex, "Failed to check youtube api job") Logging.logger.Error(ex, "Failed to check youtube api job")
tcs.SetResult(Error { tcs.SetResult(Error {
message = ex.Message Message = ex.Message
}) })
} |> Async.Start } |> Async.Start
@ -133,7 +133,7 @@ module YoutubeDlClient =
with ex -> with ex ->
Logging.logger.Error(ex, "Failed to clean youtube api job") Logging.logger.Error(ex, "Failed to clean youtube api job")
tcs.SetResult(Error { tcs.SetResult(Error {
message = ex.Message Message = ex.Message
}) })
} |> Async.Start } |> Async.Start
@ -182,18 +182,18 @@ module YoutubeDlService =
Logging.logger.Information("Sending create job to youtube client, job = {job}", job.InternalId) Logging.logger.Information("Sending create job to youtube client, job = {job}", job.InternalId)
let! result = let! result =
client.CreateJob { client.CreateJob {
url = job.Url Url = job.Url
savePath = job.SavePath SavePath = job.SavePath
} }
match result with match result with
| Ok task -> | Ok task ->
Logging.logger.Information( Logging.logger.Information(
"Created job on youtube client = {job}, externalId = {externalId}", "Created job on youtube client = {job}, externalId = {externalId}",
job.InternalId, job.InternalId,
task.task) task.Task)
let updated = { let updated = {
job with job with
State = JobState.Awaiting task.task State = JobState.Awaiting task.Task
} }
return Some updated return Some updated
@ -201,7 +201,7 @@ module YoutubeDlService =
Logging.logger.Error( Logging.logger.Error(
"Failed to create job on client = {job}, message = {message}", "Failed to create job on client = {job}, message = {message}",
job.InternalId, job.InternalId,
e.message) e.Message)
return None return None
} }
@ -275,11 +275,21 @@ module YoutubeDlService =
| Awaiting externalId -> | Awaiting externalId ->
Logging.logger.Information("Checking job = {job}", externalId) Logging.logger.Information("Checking job = {job}", externalId)
let! task = youtubeDlClient.CheckJob(externalId) let! checkResult = youtubeDlClient.CheckJob(externalId)
match task with match checkResult with
| Ok x when x.state.Equals("Finished", StringComparison.OrdinalIgnoreCase) -> | Ok x when x.State.Equals("Finished", StringComparison.OrdinalIgnoreCase) ->
Logging.logger.Information("Sending post video from job = {job}", externalId) Logging.logger.Information("Sending post video from job = {job}", externalId)
tgService.PostVideo(job.Url, job.SavePath, externalId) let infoDict =
x.VideoInfo |> Option.bind (fun v -> v.InfoDict)
let args = {
Url = job.Url
SavePath = job.SavePath
ExternalId = externalId
Title = infoDict |> Option.bind (fun i -> i.Title)
Width = infoDict |> Option.bind (fun i -> i.Width)
Height = infoDict |> Option.bind (fun i -> i.Height)
}
tgService.PostVideo(args)
postCheck() postCheck()
return! loop jobQueue None return! loop jobQueue None
@ -287,7 +297,7 @@ module YoutubeDlService =
Logging.logger.Error( Logging.logger.Error(
"Failed to receive video from youtube client, job = {job}, message = {message}", "Failed to receive video from youtube client, job = {job}, message = {message}",
externalId, externalId,
e.message) e.Message)
Logging.logger.Information("Deleting file path = {path}", job.SavePath) Logging.logger.Information("Deleting file path = {path}", job.SavePath)
File.Delete(job.SavePath) File.Delete(job.SavePath)
return! loop jobQueue None return! loop jobQueue None

View File

@ -22,7 +22,7 @@ def report_state(id: str):
def load_video(url: str, file_path: str, id: str): def load_video(url: str, file_path: str, id: str):
try: try:
opts = { opts = {
"format": 'best[height<=480][ext=mp4]/best[ext=mp4]', "format": 'best[filesize<=50M][ext=mp4]/best[height<=480][ext=mp4]/best[ext=mp4]',
"quiet": True, "quiet": True,
"outtmpl": file_path, "outtmpl": file_path,
"progress_hooks": [report_state(id)] "progress_hooks": [report_state(id)]