DavidHstar commited on
Commit
f72237c
·
verified ·
1 Parent(s): 414a97a

Update model files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .env.template +10 -0
  2. .gitattributes +7 -33
  3. .gitignore +33 -0
  4. .python-version +1 -0
  5. README.md +106 -0
  6. analyze_result.csv +113 -0
  7. docker/Dockerfile +11 -0
  8. docker/docker-compose-vali.yml +33 -0
  9. docs/images/banner.png +3 -0
  10. docs/images/graph1.png +3 -0
  11. docs/images/graph2.png +3 -0
  12. docs/images/graph3.png +3 -0
  13. docs/images/graph4.png +3 -0
  14. docs/images/graph5.png +3 -0
  15. docs/incentive_mechanism.md +734 -0
  16. docs/miner_setup.md +273 -0
  17. docs/validator_setup.md +241 -0
  18. iqf_scores.csv +53 -0
  19. iqf_scores_1.csv +75 -0
  20. iqf_scores_2.csv +31 -0
  21. iqf_scores_3.csv +8 -0
  22. iqf_scores_4.csv +14 -0
  23. iqf_scores_custom.csv +48 -0
  24. iqf_scores_video1.csv +29 -0
  25. neurons/miner.py +299 -0
  26. neurons/validator.py +1227 -0
  27. pretrained/AIM_Training_2branche_KAN-Head.pt +3 -0
  28. pretrained/AIM_Training_2branche_MLP-Head.pt +3 -0
  29. pretrained/single_branch_pretrained/Authentic.pth +3 -0
  30. pretrained/single_branch_pretrained/Synthetic.pth +3 -0
  31. run.sh +261 -0
  32. scripts/capture_video_frames.py +83 -0
  33. scripts/check_queues.py +68 -0
  34. scripts/clip_video.py +207 -0
  35. scripts/compress_miner_king1_0.sh +10 -0
  36. scripts/compress_miner_king85_1.sh +10 -0
  37. scripts/compress_miner_king85_2.sh +10 -0
  38. scripts/compress_miner_king85_3.sh +10 -0
  39. scripts/compress_server.sh +3 -0
  40. scripts/get_video_lengths.py +212 -0
  41. scripts/video_info_cap.py +68 -0
  42. services/compress/__init__.py +0 -0
  43. services/compress/encoder.py +893 -0
  44. services/compress/models/preprocessing_pipeline.pkl +3 -0
  45. services/compress/models/scene_classifier_model.pth +3 -0
  46. services/compress/scene_detector.py +614 -0
  47. services/compress/server.py +798 -0
  48. services/compress/utils/__init__.py +34 -0
  49. services/compress/utils/analyze_video_fast.py +568 -0
  50. services/compress/utils/calculate_vmaf_adv.py +734 -0
.env.template ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ BUCKET_TYPE="type of bucket. one of: backblaze, amazon_s3, cloudflare, or hippius"
2
+ BUCKET_NAME="buckent name"
3
+ BUCKET_COMPATIBLE_ENDPOINT="bucket endpoint"
4
+ BUCKET_COMPATIBLE_ACCESS_KEY="bucket personal access key"
5
+ BUCKET_COMPATIBLE_SECRET_KEY="bucket personal secret key"
6
+
7
+
8
+ #validators only
9
+ PEXELS_API_KEY="Your Pexels account api key"
10
+ WANDB_API_KEY="Your wandb api key"
.gitattributes CHANGED
@@ -1,35 +1,9 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
  *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  *.pt filter=lfs diff=lfs merge=lfs -text
2
  *.pth filter=lfs diff=lfs merge=lfs -text
3
+ docs/images/banner.png filter=lfs diff=lfs merge=lfs -text
4
+ docs/images/graph1.png filter=lfs diff=lfs merge=lfs -text
5
+ docs/images/graph2.png filter=lfs diff=lfs merge=lfs -text
6
+ docs/images/graph3.png filter=lfs diff=lfs merge=lfs -text
7
+ docs/images/graph4.png filter=lfs diff=lfs merge=lfs -text
8
+ docs/images/graph5.png filter=lfs diff=lfs merge=lfs -text
9
+ services/compress/models/preprocessing_pipeline.pkl filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
.gitignore ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ note.txt
3
+ *.log
4
+ *.db
5
+ *.mp4
6
+ *.lock
7
+ *.hevc
8
+ *.y4m
9
+ *.egg-info
10
+ *.pyc
11
+ *.mp3
12
+ *.js
13
+ *.xml
14
+ *.txt
15
+ .DS_Store
16
+ *.json
17
+ .env
18
+ venv/
19
+ .venv/
20
+ video2x/
21
+ logs/
22
+ videos/
23
+ wandb/
24
+ IQA-PyTorch/
25
+ MDTVSFA/
26
+ output/
27
+ MANIQA/
28
+ frames/
29
+ tmp/
30
+ vmaf/
31
+ services/scoring/vmaf
32
+ services/upscaling/models/
33
+ tmp_scene/
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.12
README.md ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div align="center">
2
+
3
+ # **Vidaio Subnet**: Revolutionizing Video Processing with AI-Driven Decentralization <!-- omit in toc -->
4
+
5
+
6
+ Please check our [Tweet](https://x.com/vidaio_τ) to follow us.
7
+ [Website](https://vidaio.io)
8
+ [![vidAio](./docs/images/banner.png)](https://vidaio.io)
9
+
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
+
12
+ ---
13
+
14
+ </div>
15
+
16
+ ## **Table of Contents**
17
+
18
+ 1. [Introduction](#1-introduction)
19
+ 2. [Subnet Architecture](#2-subnet-architecture)
20
+ - [Overview](#21-overview)
21
+ - [Miners](#22-miners)
22
+ - [Validators](#23-validators)
23
+ - [Synapses](#24-synapses)
24
+ - [Synthetic Query](#241-synthetic-query)
25
+ - [Organic Query](#242-organic-query)
26
+ 3. [Setup](#3-setup)
27
+ 4. [Roadmap](#4-roadmap)
28
+ 5. [Appendix](#5-appendix)
29
+ - [Technical Glossary](#a-technical-glossary)
30
+ - [References](#b-references)
31
+ - [Contact Information](#c-contact-information)
32
+
33
+ ## **1. Introduction**
34
+ Vidaio's mission is to democratise video enhancement through decentralisation, artificial intelligence, and blockchain technology. Leveraging the Bittensor ecosystem, Vidaio provides creators, businesses, and developers with scalable, affordable, and high-quality video processing solutions including upscaling and compression, while ensuring full ownership and control over their content.
35
+
36
+
37
+ ## 2. Subnet Architecture
38
+
39
+ ### 2.1 Overview
40
+ - **Miners**: Handle video processing tasks including upscaling and compression, optimizing models to ensure high-quality outputs.
41
+ - **Validators**: Assess miner performance using predefined metrics to maintain network integrity across all video processing workflows.
42
+
43
+ ### 2.2 Miners
44
+ Miners enhance video quality and optimize file sizes using AI-driven processing techniques. They can:
45
+ - Optimise open-source models or develop proprietary ones for superior upscaling and compression results.
46
+ - Handle video upscaling and compression requests from validators and end-users.
47
+ - Process both upscaling tasks (enhancing video quality) and compression tasks (reducing file size while maintaining quality).
48
+
49
+ ### 2.3 Validators
50
+ Validators ensure miners deliver consistent, high-quality results by evaluating performance through synthetic and organic queries for both upscaling and compression workflows.
51
+
52
+ ### 2.4 Synapses
53
+ #### 2.4.1 Synthetic Query
54
+ Validators benchmark miner performance using controlled datasets:
55
+ - **Upscaling**: Downscale a high-resolution video to low-resolution, then miners upscale it back to high resolution.
56
+ - **Compression**: Provide high-quality videos for miners to compress while maintaining optimal quality-to-size ratios.
57
+
58
+ - Validators assess the processed outputs using metrics VMAF and PieAPP for quality evaluation.
59
+
60
+ #### 2.4.2 Organic Query
61
+ Real-world video data uploaded by users is processed as follows:
62
+ - Videos are chunked and queued for miners.
63
+ - Miners process and apply upscaling or compression based on user requirements.
64
+ - Results are aggregated and delivered back to users.
65
+
66
+ ### 2.5 Incentive mechanism
67
+ - [Incetive Mechanism Guide](docs/incentive_mechanism.md)
68
+
69
+ ## 3. Setup
70
+ - [Validator Setup Guide](docs/validator_setup.md)
71
+ - [Miner Setup Guide](docs/miner_setup.md)
72
+
73
+ ## 4. Roadmap
74
+
75
+ ### Phase 1: Implementing the Video Processing Synapses
76
+ - Launch the subnet with AI-powered video upscaling and compression capabilities.
77
+ - Focus on real-time processing of videos for both quality enhancement and size optimization.
78
+
79
+ ### Phase 2: Developing Advanced Video Processing Models
80
+ - Build AI models for adaptive bitrate streaming and intelligent compression.
81
+ - Optimize bandwidth usage while maintaining video quality across different use cases.
82
+
83
+ ### Phase 3: Implementing the Transcode Optimization Synapse
84
+ - Introduce AI-driven transcoding for compatibility across devices.
85
+ - Evaluate miners on speed, quality, and efficiency for all processing workflows.
86
+
87
+ ### Phase 4: On-Demand Streaming Architecture
88
+ - Enable decentralized on-demand video streaming with integrated storage.
89
+ - Utilize peer-to-peer (P2P) models for redundancy and high availability.
90
+
91
+ ### Phase 5: Live Streaming Through the Subnet
92
+ - Introduce live streaming with real-time AI-powered upscaling, compression, and transcoding.
93
+ - Integrate adaptive bitrate streaming for smooth playback.
94
+
95
+ ### Phase 6: Subnet API for Real-World Integration
96
+ - Develop a RESTful API for seamless integration with external platforms.
97
+ - Include features for uploading, processing, and retrieving videos with multiple processing options.
98
+
99
+ ## 5. Appendix
100
+
101
+ ### A. Technical Glossary
102
+ - **VMAF**: [Video Multimethod Assessment Fusion](https://github.com/vidaio-subnet/vmaf)
103
+ - **Video2x**: [Vidio upscaling model](https://github.com/vidaio-subnet/video2x)
104
+ - **TOPIQ**: [Top-down Image Quality Assessment](https://arxiv.org/pdf/2308.03060v1)
105
+ - **LPIPS**: [Learned Perceptual Image Patch Similarity](https://github.com/richzhang/PerceptualSimilarity)
106
+ - **Bittensor Subnet**: [Decentralized AI Framework](https://docs.bittensor.com)
analyze_result.csv ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ file_name,metrics_resolution_width,metrics_resolution_height,metrics_frame_rate,metrics_bit_depth,input_codec,input_bitrate_kbps,bits_per_pixel,metrics_avg_motion,metrics_avg_edge_density,metrics_avg_texture,metrics_avg_color_complexity,metrics_avg_motion_variance,metrics_avg_grain_noise,metrics_avg_spatial_information,metrics_avg_temporal_information,metrics_avg_psnr,quality_noise_level,quality_sharpness,quality_contrast,quality_brightness,quality_color_saturation,quality_motion_blur,quality_compression_artifacts,quality_text_content,quality_edge_density,quality_temporal_consistency,quality_psnr
2
+ 691de276-7b47-4015-bc54-da4343889084.mp4,4208,2040,25.0,8,h264,17985.092,0.08380438753448147,0.04384737944001503,0.005426638335942742,6.8855719566345215,7.156955718994141,7.524673709889497e-05,1.1901419162750244,26.9073429107666,22.757010920983905,20.114583549810607,0.02833493985235691,0.015084956772625446,0.14730598471266507,0.46570451798664747,0.47825197173603406,0.007945261437908496,0.004781483206897974,0.5164016097567037,0.013443464362931484,0.9986614345096869,19.556071543951166
3
+ 5533624b-8b2f-476e-93c3-26ecc8abbbfc.mp4,3816,2216,29.97,8,h264,40848.835,0.16118135782046936,0.05624176500156784,0.026415047037365004,6.792716026306152,7.042492866516113,9.488612387943008e-06,2.512382745742798,38.068580627441406,17.092482685057654,21.17046433272484,0.14134825766086578,0.05944013595581055,0.1886964347338915,0.40776378246190087,0.22834675684838993,0.0767561909194802,0.014388006490965685,0.6034597344262048,0.13276803587781638,0.9937148534950766,20.368462730755084
4
+ eeb92086-2c5a-47b1-911a-9f23c3c51a83.mp4,1776,1136,24.0,8,h264,2672.796,0.055199262863215325,0.03173686158973804,0.0013392573911940107,6.271199703216553,6.502370357513428,8.143292527477933e-06,0.9688636064529419,16.720378875732422,15.422695223979982,23.325329066984917,0.01851261407136917,0.008041225373744965,0.2702982078458706,0.47260835685519825,0.49105042089002837,0.002524366355792412,0.004669796365002791,0.6441920573742758,0.0038926195121177514,0.9936521870358345,22.560553496084836
5
+ fcd1e637-3ac0-49af-920e-6bc5ae2e32cf.mp4,3696,2256,25.0,8,h264,39087.017,0.18750871653464737,0.016150920847465354,0.01093951482914249,3.45924711227417,3.2307815551757812,2.0072982843731174e-07,2.5420711040496826,50.68205261230469,16.757298690354837,23.392093338873003,0.12434706836938858,0.057585906237363815,0.4032672850400358,0.07724540338647816,0.3632161264328427,0.012838099523604844,0.005424946701774995,0.6764255555811407,0.021723695925823586,0.9999686643274331,23.093613988739953
6
+ a5f32e7e-a5f3-4a7e-b7fd-dc0fb551e243.mp4,1896,1120,29.97,8,h264,23338.264,0.3667122768740209,0.061917931399880624,0.09050651748040987,7.628411769866943,7.722901821136475,4.143976236350943e-06,4.869390487670898,82.86348724365234,25.48501617108024,18.59554961507288,0.4847494661808014,0.2182989865541458,0.10021196512715723,0.4154083440689919,0.31292339984517015,0.13150884694595139,0.016520978262027104,0.2850421313039984,0.19162546149789028,0.9980037344579182,18.556904219657582
7
+ 59bb4aa2-724b-490a-944c-eaae28496192.mp4,3632,2096,24.0,8,h264,69412.382,0.3799168785064342,0.05599570354663959,0.04387347832666375,6.056894779205322,6.095084190368652,5.721938383188476e-07,4.240983963012695,80.17750549316406,31.89957463627171,17.26237240761681,0.36625877022743225,0.16681808233261108,0.2566843861128125,0.2162887687259836,0.4824837299832892,0.05429285100772326,0.017838669940829277,0.5918705022362714,0.09003080652890338,0.9984225156964289,17.10664600652759
8
+ 64e41231-4092-46c3-906d-5170de857162.mp4,2280,1264,23.976,8,h264,23777.783,0.34412221401855625,0.06413068881366671,0.06563721407950254,6.899465084075928,6.884190559387207,1.840766088924721e-05,3.9509806632995605,52.393699645996094,20.247946697586123,19.842513662545336,0.35146045684814453,0.1335129588842392,0.16689629522158192,0.26620844908768154,0.4625996798249243,0.13319604060256127,0.02110528325041135,0.575414307128581,0.20663741533422164,0.9915836759698328,18.958802985591056
9
+ ea63ccc6-22fa-4b9b-91eb-67da4241ed6d.mp4,4304,2328,23.976,8,h264,43452.978,0.18087876209446968,0.1394414925377527,0.020270283217721227,7.215000152587891,7.374978542327881,0.002333856738159803,2.0605454444885254,46.671119689941406,51.1518810685618,12.604765054991669,0.08702545613050461,0.04784707725048065,0.1353242805091566,0.4523556977557734,0.30124959418556235,0.030720643467596676,0.008035152995338043,0.14340332336897502,0.054066424264489835,0.9066783540598498,10.662733148504527
10
+ 2b4b64a6-a07f-4bce-93eb-0b3ce53e0575.mp4,3784,2008,29.97,8,h264,38360.653,0.16845519635715442,0.05625774827874177,0.028772252427920454,6.830992221832275,7.0621795654296875,1.0617354107425008e-05,2.7690491676330566,41.869808197021484,17.13586170732713,21.158016507670354,0.16543090343475342,0.06936812400817871,0.1945907355813904,0.40093583679074135,0.19673515344582784,0.08179026318966559,0.01480244193226099,0.9143815857079082,0.14012449146332218,0.9930504353683388,20.31808044588177
11
+ 0ea244ef-3544-428f-9fff-61478f587f35.mp4,3944,2208,25.0,8,h264,12865.465,0.059094832179498485,0.11012237717646367,0.0002517583120204604,6.680722713470459,6.933882713317871,0.00017665350417377763,0.6035935878753662,8.824653625488281,28.863994801312817,16.080064259744987,0.00619066646322608,0.0023995821829885244,0.2047775009718129,0.3565399725466467,0.43710182121189506,0.0004759415635319595,0.0027012270875275135,0.2223476956374754,0.0005275395390540024,0.9722050198948968,15.090727575904303
12
+ 620bbe9f-f29d-49f4-a6bc-9f8131e754ff.mp4,2112,1200,25.0,8,h264,9701.962,0.15312440025252524,0.02665024749641018,0.011701467803030304,7.390368461608887,7.473071098327637,7.221126922539258e-07,1.7604080438613892,33.12004089355469,14.255431724624623,24.171731641019242,0.06621285527944565,0.030513504520058632,0.10253218572419898,0.4184056754225259,0.3524694452696904,0.02344039351851852,0.0073311009133855505,0.06284248737373738,0.0384597932449495,0.9987974335883343,23.30514680986035
13
+ 99010e5d-5200-4ef1-b624-88c69622c407.mp4,3944,2208,25.0,8,h264,12865.465,0.059094832179498485,0.11012237717646367,0.0002517583120204604,6.680722713470459,6.933882713317871,0.00017665350417377763,0.6035935878753662,8.824653625488281,28.863994801312817,16.080064259744987,0.00619066646322608,0.0023995821829885244,0.2047775009718129,0.3565399725466467,0.43710182121189506,0.0004759415635319595,0.0027012270875275135,0.2223476956374754,0.0005275395390540024,0.9722050198948968,15.090727575904303
14
+ 0e4ce3ab-c7c1-4dd6-9d9d-983eabf8acf3.mp4,2176,1312,29.97,8,h264,12034.455,0.1406523388637023,0.03684070183764189,0.004346403671987088,6.438337802886963,6.428807258605957,3.48921061212562e-05,1.674142837524414,29.440975189208984,15.898470576627409,22.83817060754813,0.04417116567492485,0.019086694344878197,0.27141317766616685,0.5323091666051284,0.1473271014321708,0.01067878799766858,0.0052575784890602035,0.5868236919386657,0.021302758193597563,0.9938409873174072,22.136717544720863
15
+ 708b5e08-9d59-4b41-9dba-f7735682a9b4.mp4,4032,2320,29.97,8,h264,22724.011,0.08105685321222518,0.04181160450590676,0.0013245330459770114,6.826323509216309,6.934536933898926,0.00012277416719666518,0.9263733625411987,16.907251358032227,21.607056072394094,20.696646810375498,0.01667613349854946,0.007195150945335627,0.2228156889157377,0.39245652413663423,0.7564402887392186,0.002196544027093596,0.003596095290655891,0.5808692813811348,0.003489219861795293,0.9858591746085408,19.987420021489257
16
+ dd477309-fa8a-4317-94ff-d8b54bc3cad1.mp4,3944,2168,25.0,8,h264,18113.648,0.08473634574073935,0.13552349128483232,0.005636896252329663,7.531042575836182,7.696881294250488,0.0003072400958021315,1.3717228174209595,31.410131454467773,42.14882312955888,13.435164381319346,0.03994758427143097,0.022566335275769234,0.09825435672437731,0.4887902318051936,0.4495643316600509,0.009923211554630761,0.006664993241429329,0.16810516355670657,0.018782968477504245,0.964893394653431,12.38130718901219
17
+ 8b6d7852-7f21-47df-aa03-fc8c0f29e6d3.mp4,4216,2472,25.0,8,h264,30733.787,0.1179578911896735,0.14168876190277946,0.004752718108853313,6.274033546447754,6.732527732849121,0.0004122308285955512,1.4978402853012085,33.24341583251953,54.00886169112629,11.911389710325768,0.03861694410443306,0.022224539890885353,0.15351031888166186,0.5920864945591565,0.2681580551402431,0.008266845468743922,0.005113481233517329,0.13570151413733886,0.015360606151323666,0.9985361279804811,11.7227002191702
18
+ ac12840e-397e-4d7e-ab6f-e43091e014fc.mp4,3840,2160,29.97002997002997,8,h264,14730.141,0.059256330138406636,0.08866558526140009,0.005286048418209876,7.256598472595215,7.234145164489746,0.00016839457810677365,0.8481879234313965,21.269725799560547,27.460039181811666,17.16618146535135,0.013288619928061962,0.009028784930706024,0.1450372010687532,0.30018618010797427,0.5296609669395022,0.011812829539609054,0.003580046041558186,0.22758680555555555,0.021873613522376545,0.9926868678702191,14.919262283403194
19
+ d2b97603-9f13-4042-a901-0ebe567b36d4.mp4,3840,2160,29.97002997002997,8,h264,14730.141,0.059256330138406636,0.08866558526140009,0.005286048418209876,7.256598472595215,7.234145164489746,0.00016839457810677365,0.8481879234313965,21.269725799560547,27.460039181811666,17.16618146535135,0.013288619928061962,0.009028784930706024,0.1450372010687532,0.30018618010797427,0.5296609669395022,0.011812829539609054,0.003580046041558186,0.22758680555555555,0.021873613522376545,0.9926868678702191,14.919262283403194
20
+ 5e800829-24b2-4ca7-bac4-89a02199fbec.mp4,1928,1144,29.97,8,h264,7670.417,0.11603771761102329,0.05360339340557336,0.01584053912892087,7.499912261962891,7.669247627258301,3.0794927409911567e-06,1.919191598892212,37.07494354248047,23.266718409618136,19.513456136493758,0.07946397364139557,0.04009568318724632,0.11794912686657971,0.5208322161695057,0.2232629198900251,0.03020419241892271,0.012287287972867489,0.263849394036116,0.052798925659402834,0.9977240688080937,18.971216361292488
21
+ 66a24ee2-bdd0-4c76-82c0-9bbad7d24301.mp4,3960,2120,25.0,8,h264,11641.273,0.05546632837812083,0.06259769366341925,0.004200971983990852,3.4773783683776855,3.5694053173065186,1.093904618691676e-05,1.755215048789978,40.48407745361328,46.313463127672705,14.334369893453339,0.050556451082229614,0.029183844104409218,0.32478808327863545,0.20282036653998362,0.11656953273018755,0.004839035004129344,0.002967640214289228,0.3960360682294645,0.008121783876500858,0.998263455401219,13.03948355272954
22
+ 1fda2da4-b877-415b-a0b2-9643e8c19343.mp4,3784,2008,29.97,8,h264,35429.582,0.15558382680977573,0.054290184688844834,0.012602681241208526,6.694496154785156,6.919416904449463,6.802361220513092e-05,2.3386123180389404,34.62127685546875,17.733186253125023,21.161139783311604,0.11751548200845718,0.04617464542388916,0.16985528248036963,0.47733727290811884,0.17626277868806386,0.041349226069646715,0.011168087211747965,0.7232348793690635,0.07039515037103172,0.9971271971765552,19.74997773179496
23
+ 61a7d453-d120-440f-abfe-fa05714179b4.mp4,3456,2104,24.0,8,h264,17260.798,0.0989077128038011,0.0727313055577991,0.002759624524714829,6.432045936584473,6.439814567565918,0.0001275077932272746,1.2637557983398438,24.746780395507812,31.690624780635517,16.873166012287463,0.028985248878598213,0.01408995222300291,0.2357989120564686,0.19393517288886405,0.6341521050899118,0.004715903423226776,0.005453494377434254,0.6904527457987139,0.008170132837804534,0.9847355475512977,16.241323427262568
24
+ f11482e6-3485-46a8-9ade-92b46a803067.mp4,2016,1184,30.0,8,h264,14361.645,0.20055832897629772,0.028791556212856766,0.022591480989918488,7.037728786468506,7.124327182769775,7.288890164255228e-07,2.6167094707489014,42.61678695678711,14.121280569674624,24.095855414292735,0.13105686008930206,0.05644969269633293,0.20156533900233317,0.5128782488502158,0.26388842777476357,0.05298239087301587,0.008641775387028853,0.5257810824217074,0.08510191273863149,0.9993833200979341,23.873213487190434
25
+ a201f3de-fd3d-40c5-98c8-106b4ebb66ab.mp4,4008,2224,29.97,8,h264,14194.301,0.05313305309909823,0.006167030205529131,0.0025307523442323985,6.775632381439209,7.032013893127441,1.0845960374904866e-06,0.975328266620636,19.8259220123291,5.08622773136614,33.988931964386175,0.017742745578289032,0.00878979917615652,0.24839167533013198,0.4332490194001896,0.6622677167940415,0.003750816711899941,0.0044474839232862,0.4396791698377825,0.005154259825672396,0.9987883521995367,32.68039384243157
26
+ 424df265-1080-4edf-8cfc-b05f62acfffc.mp4,3744,2176,29.97,8,h264,78212.749,0.3203288662187934,0.044290915204812126,0.05802588062468578,7.5659027099609375,7.705513954162598,1.0996226667551192e-06,4.917513847351074,77.87395477294922,24.95601362652544,19.381304863013963,0.5003300309181213,0.20496885478496552,0.09013898324527037,0.40636702643876493,0.2718818422945892,0.08350865469718871,0.015887831648190815,0.2587865257614798,0.11370963148881347,0.9993580352362319,18.992862809167306
27
+ ed1f86d1-17f2-462c-b82f-2d216774d941.mp4,3944,2208,25.0,8,h264,12849.851,0.059023112524620044,0.09063094842518908,0.00021101581562160096,6.528778076171875,6.836049556732178,0.00026608133723672546,0.6988304257392883,9.258260726928711,24.62951147116612,17.672909244450967,0.010030598379671574,0.0036405890714377165,0.2560277151441734,0.33395559014754445,0.4541880383818087,0.0008360939015786224,0.003517815455173453,0.29770278769928754,0.0017992497317517713,0.9728774016790624,17.639947833500877
28
+ 2efd6ba4-b102-4009-b197-da0cdaa634dc.mp4,4176,2264,25.0,8,h264,20832.096,0.08813655009950855,0.13059464685199518,0.0005394488783287979,7.103693962097168,7.16506814956665,0.001146094109949705,0.9840006828308105,15.54456615447998,39.08756451032873,14.196888635731524,0.01746043749153614,0.00722542405128479,0.17444827832782725,0.3013473596598065,0.2914109880184468,0.000796836993262301,0.004156547986591856,0.7164728393557441,0.0004970667824215101,0.9454958452824559,14.345898901049
29
+ 12dd2d2b-24f3-4c31-b873-fa4e53803778.mp4,3720,2000,24.0,8,h264,11168.197,0.06254590613799284,0.07419144172991776,0.001096532258064516,7.326708793640137,7.03421688079834,0.001104908264344367,0.7990032434463501,13.855031967163086,29.186652483284004,17.709958092816727,0.012685537338256836,0.00547337532043457,0.14025580058292889,0.34718295628645723,0.7360188973223698,0.0018994175627240143,0.003929015016183257,0.03343413978494622,0.0028954301075268815,0.9861516260805397,16.066609960586902
30
+ 12cb34d6-c1d8-4b85-8f0e-60afaf26ffc9.mp4,3992,2128,29.97,8,h264,82020.534,0.322161540486348,0.19442412024402633,0.031029399023611133,7.5067291259765625,7.659872531890869,0.0004892454112115741,2.5568833351135254,51.4112548828125,47.859217215903854,11.412570567182874,0.14925998449325562,0.07771322876214981,0.10661558367184167,0.4059681511346936,0.39410029608826963,0.05666945576616892,0.01621010371794303,0.1896969848217739,0.09928256418852743,0.9999994889713084,11.553677315658312
31
+ 918a9d77-727b-4255-bf27-d67c73ab109e.mp4,3848,2352,23.976,8,h264,44989.268,0.20732888701303545,0.10167412079176785,0.02935350725529297,7.006922721862793,7.049823760986328,0.00012149487561601138,2.5566444396972656,58.17711639404297,41.720730871253046,14.345926883977612,0.13242904841899872,0.0732438713312149,0.15790827165681595,0.29049045727091943,0.4459472019345969,0.044000461411175695,0.009885285670558611,0.3789205217776646,0.07739791277737706,0.977604551435959,13.299115682322263
32
+ acfbb4ee-70e5-45bf-8aa8-349e388499ed.mp4,3840,2160,29.97002997002997,8,h264,14730.141,0.059256330138406636,0.08866558526140009,0.005286048418209876,7.256598472595215,7.234145164489746,0.00016839457810677365,0.8481879234313965,21.269725799560547,27.460039181811666,17.16618146535135,0.013288619928061962,0.009028784930706024,0.1450372010687532,0.30018618010797427,0.5296609669395022,0.011812829539609054,0.003580046041558186,0.22758680555555555,0.021873613522376545,0.9926868678702191,14.919262283403194
33
+ bdc6f64e-fe6c-4f98-b37d-a8405f0674a6.mp4,3840,2160,29.97002997002997,8,h264,14730.141,0.059256330138406636,0.08866558526140009,0.005286048418209876,7.256598472595215,7.234145164489746,0.00016839457810677365,0.8481879234313965,21.269725799560547,27.460039181811666,17.16618146535135,0.013288619928061962,0.009028784930706024,0.1450372010687532,0.30018618010797427,0.5296609669395022,0.011812829539609054,0.003580046041558186,0.22758680555555555,0.021873613522376545,0.9926868678702191,14.919262283403194
34
+ 17434ca5-7913-4355-95b4-a002b59756b8.mp4,3696,1816,24.0,8,h264,30840.548,0.1914533799686608,0.09403842515430282,0.00045524867936762206,6.87848424911499,7.145921230316162,0.0005040077409196204,1.1077892780303955,15.351152420043945,25.374898278246885,17.40825033016248,0.023234063759446144,0.009360809810459614,0.2048374967811586,0.41752296928427396,0.2840800078207762,0.001776139303672343,0.008505044815440973,0.09504590031847744,0.0028014271888170566,0.9861948653276555,17.26781125231009
35
+ cee06fa7-1a4d-4fe2-bf89-72bb8aba00cb.mp4,2008,1112,25.0,8,h264,12181.487,0.2182186183324257,0.04550726201907,0.028817553526899596,7.058570861816406,7.43195104598999,7.610834804144257e-07,2.839144229888916,46.980247497558594,20.172486329717614,20.7952804170892,0.16278205811977386,0.06897910684347153,0.17154791186967153,0.4820452520102503,0.3098746465681919,0.05630953404606992,0.011344536518057188,0.1670079275225238,0.09294991795408294,0.9987712448976839,20.14113077880257
36
+ da4c28f0-dce4-49b6-b992-df633c640d41.mp4,3880,2200,29.97,8,h264,106895.356,0.41784738502951563,0.12358454021721155,0.14118765229615743,7.390982151031494,7.575097560882568,4.107447877914606e-06,5.464156150817871,84.04344177246094,32.18738204100126,15.05805100501491,0.6543295979499817,0.29104089736938477,0.13737582217231528,0.5046602271961581,0.38149802344242917,0.19782860824742268,0.03251042154928049,0.4851184786004373,0.29476563964386127,0.9992652571806605,14.78405775820291
37
+ 40949702-a809-4464-9d14-481e76abd967.mp4,1992,1080,25.0,8,h264,9412.376,0.17500327234865387,0.032604545360089596,0.03276494868362338,7.598055362701416,7.5778489112854,1.475981234591597e-07,3.0299410820007324,64.2403335571289,25.126574892633183,19.677779473362378,0.17224721610546112,0.09297648817300797,0.1250032341526129,0.3511127182416424,0.38581022650271873,0.04277542267836779,0.014354931811491648,0.16483495711240023,0.0433976647330061,0.9995155513120019,19.673463950479913
38
+ 0cb873d6-4c3a-4ced-84e9-87939b08565d.mp4,3928,2176,25.0,8,h264,16372.595,0.07662088081795855,0.0655690748284945,0.00997179469420151,7.153440952301025,7.235421657562256,0.00026610638080850317,1.4226818084716797,38.33115768432617,37.17439902946259,16.020326652970137,0.036770280450582504,0.025037065148353577,0.1093549868154569,0.49416280431220255,0.2712272513396657,0.013138959918234097,0.0052625991714497404,0.04287055167026075,0.024374108493320952,0.99460809731599,14.191413000600473
39
+ 04d651c3-3b08-4de9-befe-af12b3b3555b.mp4,1928,952,25.0,8,h264,9760.685,0.21271411572927926,0.14567895058316224,0.027217868475190903,7.379861354827881,7.489706516265869,0.00013466543611205162,2.472189426422119,50.065956115722656,45.60677688392366,12.756305164144322,0.14299236238002777,0.07285106182098389,0.12647779103585507,0.3213473471114912,0.3275197079482676,0.05156266344712159,0.016033847195406754,0.13787563780699003,0.09244841608842708,0.989902800881153,12.372051285756838
40
+ 249c9213-36ad-4280-a900-306b96685c0e.mp4,4352,2512,25.0,8,h264,47129.108,0.17244106231266393,0.023663026379807144,0.0075284772796459344,7.559027671813965,7.671954154968262,1.689348490249105e-08,1.852216124534607,30.534713745117188,10.00865205155331,26.776982325943163,0.07633254677057266,0.032172251492738724,0.11798659075028768,0.48566255651266843,0.1818267550762109,0.03302835116928937,0.00833408534526825,0.22812887844230054,0.05521717264483421,0.9998370150268331,26.456385705685307
41
+ 9ebd5e08-2435-46d7-8e11-92081982e848.mp4,4424,2264,29.97,8,h264,33097.411,0.11025942904567794,0.08548973267964877,0.015146562438098646,7.627282619476318,7.660618782043457,0.00014808484929126182,2.1128036975860596,39.34710693359375,34.54669112188891,15.952876977581399,0.07846289873123169,0.03848755359649658,0.0661154613354953,0.5784150388398565,0.3487781411412055,0.02954914381774537,0.006871931720525026,0.07993548148337477,0.054171871705250514,0.9848161683267036,14.44176895583773
42
+ dcc6663d-5979-4b94-8780-8f08f1459367.mp4,3992,2272,29.97,8,h264,45433.897,0.16714538369717863,0.023237782027220693,0.07615909636173757,7.42056131362915,7.560868740081787,7.731473646370793e-06,4.520887851715088,91.29413604736328,11.91958356243395,25.684373716053614,0.37042751908302307,0.20396246016025543,0.13353506631010406,0.339642842742698,0.3444164673070249,0.10233517210477293,0.02325626400609811,0.3504134883616999,0.13692911791893647,0.9939171225978607,23.759774352151624
43
+ 58628666-a50b-4b53-86f3-9bce954fd87a.mp4,3656,2064,25.0,8,h264,14674.839,0.07778886888707953,0.017275690657986773,0.002144610961274235,4.755399227142334,4.147421360015869,7.62657340612031e-08,1.6233251094818115,30.24703598022461,20.98089051055077,21.510398834978275,0.043102558702230453,0.019970102235674858,0.3801464858933288,0.13324564785684487,0.611579316008798,0.0024631645124081896,0.002573372796177864,0.6121459220339367,0.003477677662714366,0.9973595402976382,21.254861574329713
44
+ 2884fdc7-6b49-4008-ae33-54956784cf22.mp4,4136,2528,29.97,8,h264,96794.101,0.3088904973261177,0.199906057559813,0.03463755264059937,7.677958011627197,7.735160827636719,0.00022584952147722116,2.6518115997314453,55.998802185058594,53.27854414451389,10.790513990351288,0.1351182460784912,0.07386935502290726,0.07412626065180299,0.43634340055791515,0.3657297264831183,0.04920470995641848,0.013960901958247026,0.09381975389499625,0.09218259363599637,0.9924948222041553,10.641904721237971
45
+ bb85c24e-dfb2-4e74-a538-7b88444ba7a2.mp4,2064,1136,29.97,8,h264,6202.448,0.08826496730016038,0.012213032049264733,0.0028290138115514797,5.526223659515381,5.956391334533691,4.203832669183327e-08,1.2413911819458008,19.35202407836914,5.566881250909669,32.03723612534702,0.03160261735320091,0.012331601232290268,0.36391833500018755,0.4339056100931475,0.6477346277180488,0.006277124959056666,0.005463352271666129,0.8203375209265932,0.010636523842668414,0.9998206337412681,31.55410760665986
46
+ f0bab96c-7918-40b7-8e75-470ee67f2ce4.mp4,3912,2280,25.0,8,h264,42667.338,0.19134708319879454,0.018852291477169207,0.01145851271840132,3.321286678314209,3.2959964275360107,5.20539861462992e-07,2.6433749198913574,53.428077697753906,17.742542955666103,22.845592024866512,0.13821403682231903,0.06435994058847427,0.4485925112259545,0.05237976378738391,0.36296210795276146,0.013614579222425526,0.006106317198524873,0.8541742905320562,0.023815273741613747,0.9990574532561204,22.65985438616542
47
+ d863f7e4-dceb-4f47-8a78-dd6d1e4e880d.mp4,2200,1240,59.94,8,h264,24399.684,0.14921864679929195,0.05437209332413317,0.026887829912023466,7.528630256652832,7.624697685241699,1.1223690996102738e-05,2.919034004211426,34.91337203979492,15.516372340225086,21.779762544291827,0.1871851682662964,0.06431771069765091,0.1103539881428786,0.3952507633258582,0.3379617215801276,0.06735056207233626,0.012368634343147278,0.28846700879765397,0.10835392228739002,0.9977341311597953,20.767000095983466
48
+ ce34e0c1-7378-4fc8-96c7-44ddcbbf1487.mp4,4360,2352,59.94,8,h264,126287.219,0.2054560121788351,0.09277246707169107,0.05140101338700618,7.024899959564209,7.290508270263672,0.0001558291963913309,3.5155029296875,59.61481857299805,30.760610209878767,16.37874440928109,0.30129268765449524,0.12735895812511444,0.19224018761074277,0.5702624508009116,0.17240642905990122,0.085398268634671,0.016718280191222828,0.30273506573883374,0.15168595534544094,0.9744751753912574,15.8732601360474
49
+ 4df1a23d-73f2-4cab-b43c-5538b566a458.mp4,3840,2160,29.97002997002997,8,h264,14730.141,0.059256330138406636,0.08866558526140009,0.005286048418209876,7.256598472595215,7.234145164489746,0.00016839457810677365,0.8481879234313965,21.269725799560547,27.460039181811666,17.16618146535135,0.013288619928061962,0.009028784930706024,0.1450372010687532,0.30018618010797427,0.5296609669395022,0.011812829539609054,0.003580046041558186,0.22758680555555555,0.021873613522376545,0.9926868678702191,14.919262283403194
50
+ d69e637f-ced7-4a94-b11f-7e3a7fc584c5.mp4,3864,2416,30.0,8,h264,35921.109,0.12826094454842116,0.02301661213602439,0.009748609168689071,7.3477983474731445,7.533846855163574,1.3443314194611425e-06,1.5714272260665894,32.717960357666016,17.222614996056663,22.955994719305767,0.048455942422151566,0.02490154840052128,0.12732583616434356,0.43533010538412503,0.41831419187872226,0.017358825908710733,0.005998436671992143,0.2724476860040493,0.028833130664445452,0.99976134757579,21.705490783403683
51
+ e7f5193f-e47b-4376-977d-feb98314b110.mp4,1976,1112,29.97,8,h264,15888.659,0.24127302758345293,0.11828014829741351,0.04825368450179128,7.395143032073975,7.48813009262085,2.0638003672822308e-05,3.874429225921631,70.99330139160156,40.35615202717711,14.08389671485215,0.2872275412082672,0.13475967943668365,0.12244732204030821,0.450834061852685,0.40473016799706213,0.06874262735560539,0.01605181209743023,0.2834111253507316,0.11797573580811463,0.989530204248128,13.702267426394744
52
+ be4a76cd-c69a-433b-b55c-9dfd478c9594.mp4,4200,2128,59.94,8,h264,76809.881,0.14337698369392188,0.10147443451064636,0.04369175170068027,7.535388946533203,7.552706718444824,0.00010668694178254137,3.557075023651123,75.94049835205078,39.92224579659751,14.607808994386144,0.2657359540462494,0.13470318913459778,0.10662796044874044,0.4325665933039882,0.5377423675610595,0.05649175020885547,0.01610845203200976,0.19080513784461153,0.09768808181167205,0.9839293682385235,13.14202322363013
53
+ d31d550a-bebb-4124-bd43-8b60d0e18de8.mp4,2256,1208,23.976,8,h264,14792.29,0.22638760914445671,0.04817584689252386,0.0726737529942229,7.142242431640625,7.410560607910156,6.98028072093307e-06,4.173787593841553,85.20892333984375,21.70381119343126,20.200777240953116,0.32179033756256104,0.17825736105442047,0.1331180156782398,0.34578490073208995,0.3365648953207698,0.09813797985846916,0.02062916321059068,0.42634162713509616,0.15032264953501479,0.9993916711217388,18.366078180220182
54
+ 8fb42072-b53c-4ee3-b54d-4c2e1cf6d1ea.mp4,2032,1048,59.94,8,h264,23215.269,0.18187457664883735,0.09169163344208574,0.06755152296087034,7.2440505027771,7.581101894378662,0.00015709599938584044,4.340128421783447,69.27568817138672,32.185418920775575,16.1816205304785,0.4326435327529907,0.18270862102508545,0.1589946302332441,0.48114556349026766,0.26464688149808624,0.1195052192903368,0.0232531875371933,0.48419154845625206,0.19298147577688285,0.9696954825913656,15.691813835264224
55
+ 7fb26377-5336-45c3-8648-14c7df0d1822.mp4,2112,1080,25.0,8,h264,6974.183,0.12230259189113356,0.06908184513985167,0.015561517957351289,7.259411811828613,7.45736837387085,0.0040572175292709475,1.9812953472137451,46.00967025756836,30.231063424849967,18.777326329420777,0.07225863635540009,0.04395774379372597,0.12630594757500477,0.5686536038260235,0.377071315116488,0.02405580691170969,0.009015806329747042,0.5724709479049758,0.03894938973063973,0.9592674120012764,18.98384660344204
56
+ 6084559b-4f79-480c-ae41-bdb3d0a6987c.mp4,3864,2176,24.0,8,h264,7392.491,0.036633933606277654,0.007060472180133776,0.0033822768237729874,6.447038173675537,6.402669429779053,4.679335826993654e-07,1.1656887531280518,21.539216995239258,7.748514322343443,30.126523878507157,0.022850245237350464,0.010489453561604023,0.20668072131675053,0.23095527002449887,0.2557928800116594,0.003746998119900134,0.002171670629953345,0.6112707197915398,0.005819234963006942,0.9988106814283685,28.143217609294314
57
+ 39c98272-d26f-4139-afbf-d870b7553fab.mp4,1912,1152,29.97,8,h264,9800.944,0.1484707142443854,0.039509684100133544,0.005349891765457927,6.316155910491943,6.450854301452637,3.7880130015596525e-05,1.5432862043380737,27.982141494750977,16.21058546361702,22.550528458925847,0.037944767624139786,0.01664702780544758,0.29896926714339833,0.568221517441918,0.16585721322902708,0.014577158879590886,0.005653653138627608,0.511477219897722,0.030219638031729425,0.9934711756986034,21.820869112944806
58
+ c6b21693-9149-46a7-8062-b4e36d861374.mp4,1800,1040,25.0,8,h264,5696.982,0.12173038461538462,0.01285861980475951,0.02749284188034188,6.882208824157715,6.9071173667907715,2.307484042479586e-05,2.7927966117858887,39.489967346191406,3.6518314388026685,34.61616402154644,0.17552293837070465,0.06558234244585037,0.18862381126808184,0.37493412518853697,0.47208936651583705,0.06163301282051282,0.012805463746190071,0.32623967236467233,0.044903044871794875,0.9997276437070555,34.748478048384115
59
+ b2b4e988-b71d-4d6f-a5dd-8db222348faa.mp4,1992,1232,25.0,8,h264,6935.385,0.11303957713972775,0.050860870829255396,0.006131669535283993,7.336886405944824,7.4586381912231445,3.8484092229019065e-05,1.7237539291381836,31.403980255126953,23.852990816751063,19.455405851878844,0.05761508271098137,0.02627158723771572,0.09626810904856715,0.5841186732542597,0.19463759939597114,0.010529401154401154,0.006394967902451754,0.13623840600497225,0.01848343047514734,0.9897579143924821,18.269961000639935
60
+ b803a998-c442-4979-b250-eae2183f1ba1.mp4,3840,2160,29.97002997002997,8,h264,14730.141,0.059256330138406636,0.08866558526140009,0.005286048418209876,7.256598472595215,7.234145164489746,0.00016839457810677365,0.8481879234313965,21.269725799560547,27.460039181811666,17.16618146535135,0.013288619928061962,0.009028784930706024,0.1450372010687532,0.30018618010797427,0.5296609669395022,0.011812829539609054,0.003580046041558186,0.22758680555555555,0.021873613522376545,0.9926868678702191,14.919262283403194
61
+ 10e3a5e7-dda1-496d-84bb-f826e6567d82.mp4,2024,1160,29.97,8,h264,10593.035,0.15054459472875265,0.04069250301319929,0.005925275998364453,6.370438575744629,6.699704170227051,2.612764599108834e-05,1.745764970779419,30.442712783813477,16.2956588347572,22.448618378947685,0.06587354838848114,0.027121203020215034,0.20759801889914262,0.5772056769075355,0.15383775358310148,0.016580488165008404,0.007409828715026379,0.25078142746808413,0.027405402412430148,0.9977600003474199,21.799876719192074
62
+ b82a043d-d96b-4c3c-bd88-36673c8734fe.mp4,4272,2384,29.97,8,h264,31535.814,0.10331890801013918,0.011293258112761733,0.009357463458009703,2.474804639816284,2.908219337463379,1.0601571477324355e-07,2.572373390197754,50.87281036376953,17.336855145358008,23.234466595816986,0.11729395389556885,0.05357654020190239,0.39416468191624854,0.12791683318994926,0.19959534213405927,0.010275209155501931,0.004424559107671182,0.6027152379785335,0.014619496314380514,0.9996167497583706,23.97460201814576
63
+ f67fdc5d-c16c-434f-88b6-2885b6e16244.mp4,1704,936,29.97,8,h264,4124.463,0.08628498537862125,0.14167440865586234,0.0021714868985995748,6.9421491622924805,7.07874059677124,9.026881105823313e-05,1.47042715549469,24.149051666259766,48.281956539549896,12.537837268101267,0.03727716580033302,0.015906894579529762,0.11302669985011486,0.5849823456829463,0.23623558451241874,0.0032554956997445255,0.005870988437285026,0.34440038856118665,0.0038838354199269695,0.9677222326473224,12.12852561266281
64
+ a8fb16e1-2992-4ab7-95b7-47d48938f465.mp4,4416,2512,25.0,8,h264,47626.46,0.17173530820179084,0.024266345674041102,0.008263884081048648,7.565223693847656,7.665775299072266,4.3365062148095007e-07,1.8574262857437134,31.593847274780273,10.497816387919835,26.415430650927703,0.07536498457193375,0.032022129744291306,0.10909541513361631,0.5430451944241572,0.15671558099694924,0.03231884298363027,0.007478163422395785,0.2131279520139081,0.055001346796247574,0.9987616523664529,25.996976327787028
65
+ cf64e339-3346-4871-9275-ae8d71e69299.mp4,2224,1224,30.0,8,h264,15362.231,0.1881121450878513,0.06601673786403403,0.028416090656886255,7.328147888183594,7.462838172912598,5.204401908026663e-05,2.646209478378296,54.8980827331543,28.458309405125572,17.763753460185995,0.13267822563648224,0.06757838279008865,0.1174268615084031,0.39672566301053735,0.268810764056959,0.03962761163618125,0.011698253452777863,0.1732410640115359,0.06870165632200123,0.9941397876508482,17.797069126857515
66
+ 43a9cd6b-81bc-438d-819c-6352d7193145.mp4,3784,2008,29.97,8,h264,36206.249,0.1589944463315321,0.054590670708535086,0.02001489285985024,6.582436561584473,6.803387641906738,6.757863391028719e-05,2.5635905265808105,38.959495544433594,18.689710073875673,20.818489665846208,0.13612745702266693,0.0552828311920166,0.17039050492755023,0.45392823078893096,0.18310380040864058,0.05806890478958023,0.012364944132665793,0.731457977111989,0.10490970841791397,0.9815015737117856,19.175683075370202
67
+ b7340fda-e72e-4b12-9029-316dbd0c9ce8.mp4,3856,1936,23.976,8,h264,14123.499,0.07890839236174724,0.013572721019411503,0.011638537987380406,7.1876540184021,6.9774322509765625,0.0002915820148605531,1.9975744485855103,37.95682907104492,7.40499957796313,36.933488581518844,0.07592128217220306,0.03492182120680809,0.1509332493571397,0.3837449406136722,0.3531127043688376,0.016453866394042272,0.005977872293442488,0.0796282384863345,0.018695976111073008,0.9611574445177921,30.707378416224845
68
+ c7d92eab-29ca-490c-ab85-e18229efdf96.mp4,4344,2512,29.97,8,h264,81522.02,0.24927500735458025,0.1000569803131514,0.03622233903414623,7.442636013031006,7.571652412414551,9.676651968898144e-05,2.972356081008911,65.53794860839844,41.86743263941248,14.34625876647874,0.19248835742473602,0.10125032812356949,0.09619653533017665,0.48763471764711247,0.2784021018853362,0.04904026663421348,0.01487108568350474,0.13413356832568926,0.08725383353274449,0.9874909332758101,13.507726959867174
69
+ 70b2de9e-3c32-4b01-8505-8a78d5bccd59.mp4,4128,2344,59.94,8,h264,31615.423,0.054511102054488995,0.014116425747134979,0.004135806909278514,7.5224289894104,7.6964850425720215,1.6491528859753852e-07,1.345127820968628,23.74616241455078,10.07343460190578,27.54507604897654,0.03602388873696327,0.015233230777084827,0.12069503858303476,0.511027495028049,0.29980149562955943,0.007821250143309434,0.0043184760337074595,0.282369536052244,0.013587026169404978,0.9988060480115135,26.282205403914354
70
+ a3fa72da-8a4e-466c-9a11-fc2a67863b9e.mp4,4064,2328,29.97,8,h264,55651.917,0.19627126001383585,0.1697815446090767,0.01374202620613145,7.539480686187744,7.7134599685668945,0.0003595651628055783,1.936773657798767,40.06705093383789,45.708446787003915,12.175698271848955,0.0872727632522583,0.04557924345135689,0.09998841819431721,0.42120852938015224,0.39739355828828454,0.026862722217712475,0.01140574086457491,0.0736418196597848,0.052145853204399704,0.9822594713112537,11.54028731617105
71
+ 34e7d8b9-5fd5-44d6-ba9c-194d4e2078ae.mp4,1864,1112,25.0,8,h264,7053.062,0.1361090483836107,0.054158667984824466,0.016409361780961498,7.490847587585449,7.695387840270996,0.00029081383222468306,1.8844280242919922,49.36473083496094,27.493341599677112,18.712213241512394,0.07012951374053955,0.04689024016261101,0.11799043690058277,0.49315103837152446,0.3031222125304049,0.022825677226459177,0.009728438220918179,0.2783456711026029,0.04091823108037175,0.9759738649373356,17.535968238489765
72
+ 9d860f37-78b5-4e91-bbff-91bbfaaaa32a.mp4,2176,1128,23.976,8,h264,20564.598,0.3494423229093194,0.06460805954402254,0.06740179782019191,6.886200904846191,6.995933532714844,1.7384923576114026e-05,3.990297794342041,52.721641540527344,20.567321337157345,19.73471317473215,0.3589254915714264,0.13616614043712616,0.16749243576081677,0.2623429547776905,0.49264839289256585,0.1339137300531915,0.021455286691586178,0.4039163537755528,0.20700700908687944,0.991671204099078,18.815549968769943
73
+ 81e87893-da5e-4db2-afd1-b1bb11a03de7.mp4,1848,1080,29.97,8,h264,10031.907,0.16771466231342774,0.13550930690738697,0.010763087221420555,7.5518693923950195,7.611828804016113,0.0004041885873757899,2.7923357486724854,59.45957565307617,39.83156659420939,13.715216440202253,0.15859724581241608,0.08018888533115387,0.07077146702152066,0.4440622167310621,0.42761248822387166,0.013438953022286357,0.009651358239352703,0.2976868553257442,0.019547659130992462,0.9545209309874343,12.184221110468258
74
+ 329f1c4c-f5df-453d-9cfc-6f2cbef5142b.mp4,3840,2160,29.97002997002997,8,h264,14730.141,0.059256330138406636,0.08866558526140009,0.005286048418209876,7.256598472595215,7.234145164489746,0.00016839457810677365,0.8481879234313965,21.269725799560547,27.460039181811666,17.16618146535135,0.013288619928061962,0.009028784930706024,0.1450372010687532,0.30018618010797427,0.5296609669395022,0.011812829539609054,0.003580046041558186,0.22758680555555555,0.021873613522376545,0.9926868678702191,14.919262283403194
75
+ 0d0496e7-fe16-443b-9c16-369aa386bb4e.mp4,3824,2080,29.97,8,h264,76672.191,0.3216398955481069,0.042603158777894,0.053163999637914384,7.49969482421875,7.679389953613281,4.4176801376596195e-07,4.726040840148926,73.43344116210938,24.73622180177029,19.5001879954157,0.4522957503795624,0.18221168220043182,0.12557819123761416,0.39036642367224755,0.32774845807932124,0.07771807276579766,0.014697935121754805,0.1534577499731789,0.10635309382040553,0.9972791267899178,19.181592760557905
76
+ e199cb9b-752f-4aa1-a4b9-1885fa53a026.mp4,3704,2192,24.0,8,h264,56749.399,0.2912316005367381,0.06223628419301381,0.05104786598823918,7.1486663818359375,7.31646728515625,1.790032174237729e-06,4.499476909637451,75.83165740966797,23.24094136818146,19.14477134501647,0.3928792476654053,0.17057494819164276,0.14824753398203114,0.3366086798822518,0.15801827706509972,0.07917789934469477,0.016016355405251186,0.3772218204295481,0.1360922079700777,0.9976828925927231,18.97621105248905
77
+ 4cf5400c-d443-4509-8128-dfdcd50a9fcf.mp4,4272,2384,29.97,8,h264,31535.814,0.10331890801013918,0.011293258112761733,0.009357463458009703,2.474804639816284,2.908219337463379,1.0601571477324355e-07,2.572373390197754,50.87281036376953,17.336855145358008,23.234466595816986,0.11729395389556885,0.05357654020190239,0.39416468191624854,0.12791683318994926,0.19959534213405927,0.010275209155501931,0.004424559107671182,0.6027152379785335,0.014619496314380514,0.9996167497583706,23.97460201814576
78
+ e4bb727c-b404-4141-bfbe-af71df54c3ff.mp4,3840,2160,29.97002997002997,8,h264,14730.141,0.059256330138406636,0.08866558526140009,0.005286048418209876,7.256598472595215,7.234145164489746,0.00016839457810677365,0.8481879234313965,21.269725799560547,27.460039181811666,17.16618146535135,0.013288619928061962,0.009028784930706024,0.1450372010687532,0.30018618010797427,0.5296609669395022,0.011812829539609054,0.003580046041558186,0.22758680555555555,0.021873613522376545,0.9926868678702191,14.919262283403194
79
+ abcae1d5-f557-46b6-8704-3b34ecdb3855.mp4,2128,1080,23.976,8,h264,9758.537,0.17709757077345611,0.06317856101992454,0.021328842940685044,7.17816162109375,7.319029808044434,6.02598328172642e-05,2.1986310482025146,35.48875045776367,21.518270390254973,19.59833060833165,0.11287132650613785,0.04843633249402046,0.16474877279461442,0.3239840455350431,0.46334531351526964,0.0428242191125963,0.013388105357686678,0.34700553467000833,0.08521303258145363,0.9898198109653218,18.315084527253653
80
+ 93495912-fd6c-435a-9fcb-c9cc978c122e.mp4,2008,1248,29.545,8,h264,12599.66,0.17017530504579095,0.09179893751414653,0.026034882904280316,7.28030252456665,7.439886569976807,2.6556406274797802e-05,2.5419487953186035,45.587520599365234,28.28280527215062,16.845219875476445,0.1255028247833252,0.05773575231432915,0.14505126243363076,0.5325459562996547,0.2646494876660611,0.045352377881635854,0.007195856577406327,0.08977498127149526,0.07977624757380733,0.9939426353785461,15.28308285240923
81
+ e2dd775b-6dbe-49fd-876e-3e9b4a2e80cd.mp4,3912,2208,23.976,8,h264,44342.566,0.21411454779604497,0.09509652238055057,0.029812093410094543,6.707069396972656,7.182584285736084,0.00012590194600901514,2.597018003463745,58.731727600097656,40.9387849600393,14.626761505678672,0.1354481726884842,0.07431226223707199,0.1634576248055144,0.3116403942588515,0.4550657866970771,0.04392097151833082,0.00971723503122727,0.11038838752062285,0.07683003662087667,0.9770803141912644,13.593667907575028
82
+ a508a47e-295b-4be0-ab3f-4b6478dacc65.mp4,4360,2352,59.94,8,h264,126287.219,0.2054560121788351,0.09277246707169107,0.05140101338700618,7.024899959564209,7.290508270263672,0.0001558291963913309,3.5155029296875,59.61481857299805,30.760610209878767,16.37874440928109,0.30129268765449524,0.12735895812511444,0.19224018761074277,0.5702624508009116,0.17240642905990122,0.085398268634671,0.016718280191222828,0.30273506573883374,0.15168595534544094,0.9744751753912574,15.8732601360474
83
+ 733704b4-12f7-4977-b920-6896bc9ca77c.mp4,3632,2112,25.0,8,h264,35757.395,0.18646018451308236,0.023405982271658993,0.0114293402082499,7.527238368988037,7.664534568786621,4.917982302283578e-07,1.9592946767807007,32.69782638549805,10.107730053748933,26.739501253563247,0.08349424600601196,0.03576952591538429,0.12351014467901057,0.5205489246068594,0.23741459897574257,0.04216461663718685,0.008015718931953112,0.1772868761958795,0.06795654785742891,0.9985238316412702,26.44615321681865
84
+ 07484eba-7a60-4754-ba0b-f9c0744af11b.mp4,3744,2320,29.97,8,h264,42375.281,0.16278036697297554,0.04680557474334414,0.021254674145299142,7.1397528648376465,7.319797515869141,0.00018653402036211456,2.0145583152770996,38.731197357177734,20.055538777924156,20.979329955462454,0.09581049531698227,0.04451819881796837,0.14676288494422374,0.3145919473712218,0.5450120102012216,0.04606807673887415,0.010496880548695723,0.3294798113763631,0.07769488653109342,0.9906222568234485,20.052267598182596
85
+ 026fe674-d8c4-40a4-96c5-0cb6f1fb7c06.mp4,4080,2152,29.97,8,h264,18497.378,0.07029444373790415,0.015048249600882703,0.006021302573073839,7.39658260345459,7.519859313964844,5.888028856840528e-08,1.1937366724014282,29.523151397705078,9.507835113867262,27.918117677847636,0.0281049907207489,0.016799675300717354,0.1342756832257432,0.3967183437811308,0.33484082860371595,0.010203382778142237,0.004288815738012393,0.2226463602789319,0.017361243986442162,0.9997948641607675,26.13181026051079
86
+ af18139c-acaf-4c43-977a-9a86fb3f3602.mp4,3920,2208,24.0,8,h264,12827.599,0.061751711271012026,0.08963311173654244,0.0007833296362023069,7.278562068939209,6.828987121582031,0.0003169389877970123,0.9030637741088867,14.510713577270508,32.08218969560803,16.33525454985052,0.015057296492159367,0.006208827253431082,0.14356219282733998,0.3124642683160221,0.8005967936691585,0.0014279398353544317,0.0033351377739260593,0.0207769520851819,0.0021434117125110914,0.988551496232551,14.702759137373825
87
+ fd4e4b88-600d-4a77-acdc-a1315ce301cc.mp4,4568,2672,24.0,8,h264,17424.241,0.05948124889122805,0.09230391342392479,0.0007026391612571703,7.3348565101623535,7.044018745422363,0.0003478555279513488,0.9210092425346375,14.63671588897705,32.29689578650071,16.21557571299345,0.01604432426393032,0.006564124953001738,0.13583912832934256,0.3137757971488773,0.7292011663634032,0.0011647567387117186,0.0032998408811787763,0.12096363315400722,0.0016134270425873297,0.9884601788362083,14.531413968873947
88
+ a75909eb-460b-4eb8-ab69-1f81413fde39.mp4,3504,2216,24.0,8,h264,12771.349,0.06853172723523125,0.07632779644608449,0.0006379764024199265,7.036263465881348,7.020784854888916,0.00013900804781572326,0.7060607075691223,11.723587036132812,27.328210487867413,17.65899654426508,0.009950431063771248,0.0039873127825558186,0.18371826557134377,0.32048653231465507,0.7251103568436426,0.000994694390869778,0.0028258475164572396,0.043288330613388736,0.0012447738943013038,0.9744050005001919,17.147321929463487
89
+ d4346f5c-0084-437f-a2ab-3eaedfc55c11.mp4,3616,2064,29.97,8,h264,18160.488,0.08119002157127286,0.01640264128993163,0.0017206311741098993,7.56039571762085,7.616456508636475,1.0569616206471612e-07,1.204588532447815,17.363248825073242,6.7892021762389385,30.0987914517863,0.03240012750029564,0.01197904348373413,0.08993872157304633,0.4335584335421187,0.3625504576623417,0.005593768579497382,0.005058024854709704,0.10961778400905531,0.010008743975955272,0.9993855382951184,29.301524936051145
90
+ a3a1e82b-d8da-433a-bffc-fe9aa6c0d6b8.mp4,1904,1080,24.0,8,h264,3517.439,0.07127293336316008,0.037792377435723744,0.0050058356676003736,4.719388008117676,4.7925543785095215,4.7425856449873786e-07,1.670793890953064,34.762107849121094,28.021119450221647,18.698712076808796,0.04882233217358589,0.025018975138664246,0.32413395355304236,0.16579031467343655,0.3141739777792017,0.006094382197323373,0.003943651216104627,0.4692836393816786,0.009816808667911608,0.997371655361491,17.778837999654417
91
+ 5fe0ff99-37a3-4760-81a4-f441876cb867.mp4,3840,2160,29.97002997002997,8,h264,14730.141,0.059256330138406636,0.08866558526140009,0.005286048418209876,7.256598472595215,7.234145164489746,0.00016839457810677365,0.8481879234313965,21.269725799560547,27.460039181811666,17.16618146535135,0.013288619928061962,0.009028784930706024,0.1450372010687532,0.30018618010797427,0.5296609669395022,0.011812829539609054,0.003580046041558186,0.22758680555555555,0.021873613522376545,0.9926868678702191,14.919262283403194
92
+ 221cab7a-eb1a-4b14-9568-9ac4a4ec9330.mp4,1672,1096,29.97,8,h264,25545.608,0.46513891276344854,0.1205145017267201,0.11582920057276569,7.7315778732299805,7.773962497711182,1.0196675601302348e-05,6.703019618988037,100.35294342041016,41.351868413868374,13.893107574016607,0.9161150455474854,0.37918129563331604,0.05437708776697476,0.5162277035227647,0.293133004869604,0.15879368502543684,0.02824004056553046,0.3147559197429539,0.24483168459469842,0.9938609909003939,12.963095414146789
93
+ 3e9e0378-5364-4b63-89a4-c8be5c4b826e.mp4,3840,2160,29.97002997002997,8,h264,14730.141,0.059256330138406636,0.08866558526140009,0.005286048418209876,7.256598472595215,7.234145164489746,0.00016839457810677365,0.8481879234313965,21.269725799560547,27.460039181811666,17.16618146535135,0.013288619928061962,0.009028784930706024,0.1450372010687532,0.30018618010797427,0.5296609669395022,0.011812829539609054,0.003580046041558186,0.22758680555555555,0.021873613522376545,0.9926868678702191,14.919262283403194
94
+ 494e83e7-548e-408f-af3d-d702cc6ae594.mp4,1752,944,23.976,8,h264,5610.42,0.14148570006040403,0.07415588328242063,0.010378574607228542,7.490828037261963,7.702061653137207,7.470712731646609e-05,1.761763334274292,34.92809295654297,32.01824093913225,16.73719571119993,0.06444501131772995,0.03184501454234123,0.12185876870481349,0.5155074374479302,0.34469482376058025,0.02067350791992364,0.008139917006095251,0.39447451500141884,0.038919201300208955,0.9925388100348878,15.28414787432056
95
+ 814a4743-06ee-4a4b-9e77-e11bee77ceeb.mp4,1752,1064,23.976,8,h264,4392.675,0.09828259450871203,0.032987469576270786,0.0056733228619493935,7.031918525695801,6.870492458343506,1.6726312174518266e-05,1.5741097927093506,26.83880615234375,14.938224865451867,23.5008968614912,0.04748789593577385,0.02055317908525467,0.1985460133073029,0.22976535062474882,0.7212429583505275,0.010479251782424098,0.007446895043055217,0.7969974880122681,0.018379102722559826,0.9985523841836662,22.36412702479837
96
+ 2b8983b8-2396-4020-a6db-35b19bb2d287.mp4,4008,2088,29.97,8,h264,40148.403,0.160074931521024,0.05797263642180743,0.02584557895702847,6.822464942932129,7.055733680725098,7.172180051108346e-06,2.4900307655334473,36.52580261230469,17.285564280269153,20.99822895336429,0.14220792055130005,0.058163028210401535,0.20122307188328248,0.3787347649628497,0.2031581901056525,0.07335659938105908,0.014992917577425638,0.7752869102153293,0.12570052662873488,0.9969557560560646,20.24408139521997
97
+ e0ece203-f76b-40b9-8285-cb9fd999c5c4.mp4,3912,2192,24.0,8,h264,40776.066,0.19813202848618514,0.09945711239804685,0.03496622315017987,7.536346435546875,7.638005256652832,1.6951137484897176e-05,2.46947979927063,53.10587692260742,32.36660032537817,15.852002036949575,0.12401280552148819,0.06782073527574539,0.10089832206280241,0.3571532536652205,0.37619714213386257,0.053949238788629665,0.012217029929161072,0.2409559114385085,0.09947692762676698,0.9933512871772088,15.267127113506389
98
+ b872dec9-a527-443e-807b-8a650fb44aa5.mp4,3672,2200,29.97,8,h264,26838.802,0.11085391359538055,0.043140750910683145,0.007107075658546247,7.114101409912109,7.103025913238525,1.4296802407989588e-05,1.8586738109588623,30.439306259155273,17.2225639439719,21.915730799124205,0.07348591089248657,0.0299688708037138,0.12240659163278807,0.34816310030977465,0.42257273296854225,0.020484997029114677,0.008512660240133604,0.083881626724764,0.03608437314319667,0.9853751111654803,20.685591731501987
99
+ 8f0464f5-2e81-4d50-b4bb-0667fadd55b6.mp4,1944,1000,30.0,8,h264,28629.622,0.49090572702331964,0.092782616194626,0.10684485596707818,7.022085666656494,7.256712436676025,1.2697564529638103e-05,6.1641411781311035,106.89842224121094,36.91257181957464,15.29488864155018,0.7986264824867249,0.35498401522636414,0.15702301984755945,0.5378053457597031,0.22792991675408159,0.1251349451303155,0.03104855741063754,0.33499417009602195,0.18092052469135803,0.986462531267651,14.750361283262205
100
+ 890c5911-8ee1-4faa-a801-c605f0dde8e0.mp4,3840,2160,29.97002997002997,8,h264,14730.141,0.059256330138406636,0.08866558526140009,0.005286048418209876,7.256598472595215,7.234145164489746,0.00016839457810677365,0.8481879234313965,21.269725799560547,27.460039181811666,17.16618146535135,0.013288619928061962,0.009028784930706024,0.1450372010687532,0.30018618010797427,0.5296609669395022,0.011812829539609054,0.003580046041558186,0.22758680555555555,0.021873613522376545,0.9926868678702191,14.919262283403194
101
+ ac024e5d-540e-4905-810d-f1b33b0aa5c9.mp4,4264,2488,24.0,8,h264,18365.504,0.07213134615887341,0.012953859447246502,0.0136907625646254,7.144860744476318,7.201314449310303,3.7654594462563302e-06,1.621660828590393,41.49378204345703,13.670468671451426,25.199696043170125,0.048048000782728195,0.03361654654145241,0.18680149284170744,0.24222968787254692,0.3719897684756554,0.023478016555765358,0.009723028168082237,0.4041484805616051,0.018766062088644633,0.9968599666509461,23.98313975966201
102
+ 31f7bc2c-838f-43c2-9cf8-745cc9ce27ab.mp4,4072,1984,25.0,8,h264,19146.36,0.09479747607579694,0.04550693960812429,0.0067543045741174975,7.1837944984436035,7.262981414794922,3.760470042574669e-05,1.2780088186264038,28.892736434936523,25.865269563431205,19.12290828994152,0.03580651804804802,0.018519092351198196,0.14137635115849687,0.45802809033771,0.30936963102122994,0.012009179196505905,0.005717673959831397,0.43970724538944167,0.021449097693136444,0.9821693564696935,17.673464012040057
103
+ 2b0347f7-dc82-40d1-a16f-1e835c93471d.mp4,4312,2080,24.0,8,h264,25544.905,0.11867273816213549,0.2003347633852492,0.0045769855144855145,7.405059814453125,7.60549259185791,0.00036262674436563104,1.1897826194763184,28.893930435180664,54.28900251775701,10.69149180162558,0.028370706364512444,0.016952944919466972,0.11795479119000803,0.48218439867205626,0.5729273339580588,0.006566275985918843,0.0061726671022673445,0.2979950109414395,0.012031774029541887,0.9680268523493314,11.099574128495348
104
+ 5cbb381b-c803-4c00-9f7c-eb34a93f0f11.mp4,3784,2008,29.97,8,h264,36618.783,0.16080602904817673,0.06542768694838852,0.019816163464535096,6.789691925048828,7.012326717376709,2.6450260263097307e-05,2.542257785797119,37.257537841796875,20.466320563078575,19.71761871395306,0.15050595998764038,0.060365621000528336,0.1814356638339386,0.42071156347264976,0.1782449488412061,0.0653040586070096,0.013540097512304783,0.8411135233554857,0.11950388193526107,0.9788613655686915,17.629037236788477
105
+ 6f22e710-af89-4704-8e14-022222e9748b.mp4,3720,2272,59.94,8,h264,22322.13,0.044062347122982445,0.015392131575294059,0.008948607640466455,7.460536956787109,7.5750732421875,4.295919040300286e-06,1.6437866687774658,32.09257507324219,11.993583547762306,26.12937973756229,0.04855892062187195,0.023132318630814552,0.11537393971455372,0.46319905696831204,0.3847542499002731,0.013721311966782774,0.0031315109226852655,0.18334130004038562,0.02164215129486597,0.9949059255782428,25.658407466420066
106
+ f4f67a32-a8f5-43ac-93fb-6a41a45696de.mp4,3920,2072,24.0,8,h264,41024.51,0.21045359203044153,0.021401841745385414,0.11354254491371837,7.544743537902832,7.687009334564209,7.526890999826134e-07,5.495125770568848,111.36131286621094,8.965509817871082,27.70741804902253,0.5899763107299805,0.32699307799339294,0.12487122837721032,0.4635716519555055,0.31847648325739564,0.1427197012975074,0.030318242808183033,0.24108341212933046,0.10722546982113308,0.9999854710223858,27.01278655262587
107
+ ec818c60-f6b6-4272-8688-db52a71f64c7.mp4,4016,2304,24.0,8,h264,25400.055,0.11437924787395556,0.20554661131905522,0.003539487881806109,7.454084873199463,7.498093605041504,0.0008068909499226956,1.0435192584991455,25.511754989624023,54.08541538015693,10.652868069190628,0.01988261751830578,0.01239417027682066,0.11179273230338858,0.4369010138639342,0.533342198844871,0.005289785591430574,0.005997409888853629,0.2147454741220304,0.009756330580455953,0.9224177695196881,10.56287084864626
108
+ 9a049c4c-bb40-4692-8b95-cb2ca94d339f.mp4,2072,1168,29.97,8,h264,6455.45,0.08900352041009824,0.009832887361719956,0.005371522452001904,7.566030025482178,7.583876132965088,2.1056042965511564e-06,1.864336609840393,31.31435775756836,4.709945760801032,33.683942863294355,0.05904766917228699,0.02554207108914852,0.07794002734583987,0.45699018562132987,0.46636608009240965,0.009612979540205567,0.004641825954119365,0.04973108504786589,0.013096381300576505,0.9963151955987875,32.48977789954205
109
+ bd22f174-e591-4d7a-a5fa-40447e18191e.mp4,1840,1008,59.94,8,h264,12423.375,0.11174922010519837,0.05327088904704394,0.005766369047619048,7.4864821434021,7.577877044677734,7.612468171226111e-06,2.7005228996276855,32.72980499267578,16.431595009157522,21.566036022415087,0.172185018658638,0.058717746287584305,0.09891754670975317,0.413866736610103,0.3300058145238772,0.04619978577179664,0.011242882038156191,0.050635136300897145,0.07710570867839889,0.9926592185787358,19.394910594585504
110
+ 223c0d7d-c60b-4ce0-b867-a6292c9aa465.mp4,3784,2008,29.97,8,h264,38253.751,0.16798575186148923,0.0678976499847726,0.0288653262215409,6.804425239562988,7.0386223793029785,1.0396655579733884e-05,2.766972064971924,40.70533752441406,21.01144195852966,19.44021486807754,0.1735588163137436,0.07103276997804642,0.1924423491662839,0.38597894122497145,0.1846702573626785,0.08510461325943582,0.01528609481950601,0.9235667442983527,0.1461225657623207,0.9917693076510441,18.361035670056648
111
+ 992adda7-c2d9-4447-904a-e8d8157adde8.mp4,4112,2280,25.0,8,h264,23346.907,0.09960964485630419,0.05243066451869163,0.0065050302068400566,6.603093147277832,6.729841709136963,0.00026400312456757177,1.3120918273925781,33.72609329223633,31.345461426843613,17.721685776054223,0.030552295967936516,0.0189980436116457,0.22162180790489205,0.21826521987045897,0.41920875111598327,0.009328815106833232,0.005134458498408397,0.5314387216419778,0.016115487831933922,0.9950573907373722,17.40613349465314
112
+ 4ed3e002-a64d-4ab2-ac0a-3c46c2fcee77.mp4,3640,1920,25.0,8,h264,18403.842,0.10533334478021979,0.0518306550985779,0.007591288919413919,6.731770992279053,6.858646392822266,0.0002865738990085321,1.6696780920028687,39.051395416259766,31.16924249517514,17.799405625266896,0.04685891792178154,0.02683914639055729,0.20264662050472868,0.283926594970253,0.4202275605487922,0.010581978785103785,0.005136539849142234,0.20384500915750914,0.018152472527472527,0.995746789821698,17.49948355615558
113
+ c9895c78-cafb-424c-88c4-3959e7f51de5.mp4,3640,1920,25.0,8,h264,16884.299,0.0966363266941392,0.04825374240240968,0.007400927197802198,6.651494026184082,6.790753364562988,8.290628547042521e-05,1.619092583656311,38.700714111328125,28.234399211131237,18.456720883807584,0.04381901025772095,0.025941243395209312,0.21331411486039786,0.2665905835892049,0.4055052751068376,0.008540426587301588,0.004679830279201269,0.23979681776556774,0.014388307005494507,0.9819162065467213,18.323213503240172
docker/Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY ../requirements/common_requirements.txt /app/
6
+
7
+ RUN pip install --no-cache-dir -r common_requirements.txt
8
+
9
+ COPY ../services/ /app/services/
10
+
11
+ CMD ["python", "--version"]
docker/docker-compose-vali.yml ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.9'
2
+ services:
3
+ redis:
4
+ image: redis:latest
5
+ container_name: redis
6
+ ports:
7
+ - "6379:6379"
8
+ scoring_server:
9
+ build:
10
+ context: ./
11
+ dockerfile: Docker/Dockerfile
12
+ container_name: vali_scoring_server
13
+ command: ["python", "services/scoring/server.py"]
14
+ volumes:
15
+ - ../services:/app/services
16
+
17
+ video_scheduler_worker:
18
+ build:
19
+ context: ./
20
+ dockerfile: Docker/Dockerfile
21
+ container_name: vali_video_scheduler_worker
22
+ command: ["python", "services/video_scheduler/worker.py"]
23
+ volumes:
24
+ - ../services:/app/services
25
+
26
+ video_scheduler_server:
27
+ build:
28
+ context: ./
29
+ dockerfile: Docker/Dockerfile
30
+ container_name: vali_video_scheduler_server
31
+ command: ["python", "services/video_scheduler/server.py"]
32
+ volumes:
33
+ - ../services:/app/services
docs/images/banner.png ADDED

Git LFS Details

  • SHA256: 74e612b76ad5194c7887e9c23ca05e3a74a25b9ac22fbd315c2291bbaee9870f
  • Pointer size: 132 Bytes
  • Size of remote file: 1.6 MB
docs/images/graph1.png ADDED

Git LFS Details

  • SHA256: e442e0f86eed3b34d66d250976ef9876b65ccf5dd80219f09c49bdea191b0360
  • Pointer size: 131 Bytes
  • Size of remote file: 100 kB
docs/images/graph2.png ADDED

Git LFS Details

  • SHA256: a381cf7e57b3fddddb23c15a13417baf530aa18e0bd6cc1451ef64c7a4696d25
  • Pointer size: 131 Bytes
  • Size of remote file: 128 kB
docs/images/graph3.png ADDED

Git LFS Details

  • SHA256: eb56f386c56e10f70d4d1c84091d7a52257fd36b07ae22d9eeb6ee4d83b9cfaf
  • Pointer size: 131 Bytes
  • Size of remote file: 159 kB
docs/images/graph4.png ADDED

Git LFS Details

  • SHA256: c28206d078f7ddde6579d1a349b19b5fd4852375b356e0e6cf9aaa09c966467d
  • Pointer size: 131 Bytes
  • Size of remote file: 277 kB
docs/images/graph5.png ADDED

Git LFS Details

  • SHA256: 9ec423634b42c99b958055508d5c1a271c1bfb5e0c79e4a1ff8fdebe7d01a32f
  • Pointer size: 131 Bytes
  • Size of remote file: 287 kB
docs/incentive_mechanism.md ADDED
@@ -0,0 +1,734 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # VIDAIO Subnet Validation & Incentive Mechanism
2
+
3
+ ## Table of Contents
4
+ - [Overview](#overview)
5
+ - [Task Types](#task-types)
6
+ - [Upscaling Tasks](#upscaling-tasks)
7
+ - [Compression Tasks](#compression-tasks)
8
+ - [Quality Validation Metrics](#quality-validation-metrics)
9
+ - [VMAF (Video Multi-Method Assessment Fusion)](#vmaf-video-multi-method-assessment-fusion)
10
+ - [PIE-APP (Perceptual Image-Error Assessment)](#pie-app-perceptual-image-error-assessment-through-pairwise-preference)
11
+ - [Upscaling System](#upscaling-system)
12
+ - [Upscaling Scoring](#upscaling-scoring)
13
+ - [Content Length Scoring](#content-length-scoring)
14
+ - [Upscaling Final Score](#upscaling-final-score)
15
+ - [Upscaling Penalty & Bonus System](#upscaling-penalty--bonus-system)
16
+ - [Compression System](#compression-system)
17
+ - [Compression Scoring](#compression-scoring)
18
+ - [Compression Penalty & Bonus System](#compression-penalty--bonus-system)
19
+ - [Implementation Guidelines](#implementation-guidelines)
20
+ - [Technical Specifications](#technical-specifications)
21
+ - [Mathematical Properties](#mathematical-properties)
22
+
23
+ ---
24
+
25
+ ## Overview
26
+
27
+ The VIDAIO subnet validation mechanism ensures quality and reliability of miners' contributions through comprehensive assessment systems for different video processing tasks. The mechanism evaluates video processing performance using industry-standard **VMAF** and **PIE-APP** metrics, combined with advanced scoring systems that reward consistency and penalize poor performance.
28
+
29
+ **Key Features:**
30
+ - 🎯 **Multi-task support** for upscaling and compression operations
31
+ - 📊 **Quality validation** using industry-standard VMAF and PIE-APP metrics
32
+ - 🏆 **Performance-based incentive systems** with exponential rewards
33
+ - 📈 **Historical performance tracking** with rolling 10-round windows
34
+ - ⚖️ **Balanced penalty/bonus multipliers** encouraging sustained excellence
35
+ - 📦 **Dynamic content processing** (5s to 320s capability)
36
+
37
+ ---
38
+
39
+ ## Task Types
40
+
41
+ ### Upscaling Tasks
42
+
43
+ Upscaling tasks require miners to enhance video quality by increasing resolution while maintaining or improving visual fidelity. These tasks focus on **quality improvement** as the primary objective.
44
+
45
+ **Key Characteristics:**
46
+ - **Primary Goal**: Quality enhancement and resolution improvement
47
+ - **Quality Metrics**: PIE-APP for scoring, VMAF for threshold validation
48
+ - **Content Length**: Dynamic processing durations (5s to 320s)
49
+ - **Scoring Focus**: Quality improvement with content length consideration
50
+
51
+ ### Compression Tasks
52
+
53
+ Compression tasks require miners to reduce video file sizes while maintaining quality above specified VMAF thresholds. These tasks focus on **efficiency optimization** balancing file size reduction with quality preservation.
54
+
55
+ **Key Characteristics:**
56
+ - **Primary Goal**: File size reduction with quality maintenance
57
+ - **Quality Metrics**: VMAF for both scoring and threshold validation
58
+ - **Compression Rate**: File size reduction efficiency measurement
59
+ - **Scoring Focus**: Compression efficiency with quality threshold compliance
60
+
61
+ ---
62
+
63
+ ## Quality Validation Metrics
64
+
65
+ ### VMAF (Video Multi-Method Assessment Fusion)
66
+
67
+ VMAF serves as the foundational video quality assessment metric, comparing frame-by-frame quality between original and processed videos. This metric provides objective measurement of subjective video quality as perceived by humans.
68
+
69
+ #### Key Characteristics
70
+ - **Purpose**: Frame-by-frame quality comparison
71
+ - **Range**: 0-100 (higher values indicate better quality)
72
+ - **Usage**: Threshold validation and quality scoring
73
+ - **Industry Standard**: Widely adopted in professional video processing
74
+
75
+ #### Mathematical Implementation
76
+
77
+ **Harmonic Mean Calculation:**
78
+ ```
79
+ H = n / (1/S_1 + 1/S_2 + ... + 1/S_n)
80
+ ```
81
+
82
+ **Where:**
83
+ - `S_i`: VMAF score for frame `i` (i = 1, 2, ..., n)
84
+ - `n`: Total number of frames in the video
85
+ - `H`: Harmonic mean emphasizing poor-quality frame impact
86
+
87
+ #### Why Harmonic Mean?
88
+
89
+ The harmonic mean approach provides several critical advantages:
90
+
91
+ | Advantage | Description | Impact |
92
+ |-----------|-------------|---------|
93
+ | **Sensitivity to Low Values** | Heavily penalizes poor-quality frames | Ensures consistent quality |
94
+ | **Quality Consistency** | Prevents miners from neglecting frame quality | Maintains processing standards |
95
+ | **Threshold Function** | Validates authentic processing processes | Prevents gaming attempts |
96
+
97
+ > **Note**: VMAF scores are calculated using 5 random frames for both upscaling and compression tasks.
98
+
99
+ ---
100
+
101
+ ### PIE-APP (Perceptual Image-Error Assessment through Pairwise Preference)
102
+
103
+ PIE-APP provides deep learning-based perceptual similarity assessment between original and processed video frames, serving as the primary quality scoring mechanism for upscaling tasks.
104
+
105
+ #### Technical Specifications
106
+
107
+ | Parameter | Value | Description |
108
+ |-----------|-------|-------------|
109
+ | **Scale Range** | (-∞, ∞) | Theoretical range |
110
+ | **Practical Range** | 0 to 5+ | Positive values (lower = better) |
111
+ | **Processing Interval** | Every frame | Default frame sampling rate |
112
+ | **Implementation** | Deep learning-based | Advanced perceptual assessment |
113
+
114
+ #### Calculation Process
115
+
116
+ **Step 1: Raw PIE-APP Score**
117
+ ```
118
+ PIE-APP_score = (Σ abs(d(F_i, F'_i))) / n
119
+ ```
120
+
121
+ **Where:**
122
+ - `F_i`: Frame `i` from original video
123
+ - `F'_i`: Corresponding frame `i` from processed video
124
+ - `d(F_i, F'_i)`: Perceptual difference between frames
125
+ - `n`: Number of processed frames (4 random frames)
126
+
127
+ **Step 2: Score Normalization**
128
+ ```
129
+ 1. Cap values: max(Average_PIE-APP, 2.0)
130
+ 2. Sigmoid normalization: normalized_score = 1/(1+exp(-Average_PIE-APP))
131
+ 3. Final transformation: Convert "lower is better" to "higher is better" (0-1 range)
132
+ ```
133
+
134
+ #### Visual Score Transformation
135
+
136
+ The PIE-APP scoring system uses sophisticated mathematical transformations:
137
+
138
+ - *Sigmoid normalization function for PIE-APP scores*
139
+ !<img src="./images/graph2.png" alt="Sigmoid Function" width="1200" height="800">
140
+
141
+ - *Final score transformation converting to 0-1 range*
142
+ !<img src="./images/graph1.png" alt="Final Score Function" width="1200" height="800">
143
+
144
+ ---
145
+
146
+ ## Upscaling System
147
+
148
+ ### Upscaling Scoring
149
+
150
+ #### Quality Score Calculation
151
+
152
+ **VMAF Threshold Validation:**
153
+ ```
154
+ If VMAF_score < VMAF_threshold:
155
+ S_Q = 0 (Zero score for quality violation)
156
+ Else:
157
+ S_Q = PIE-APP_Final_Score
158
+ ```
159
+
160
+ **Where:**
161
+ - `S_Q`: Quality score for upscaling
162
+ - `VMAF_score`: Achieved VMAF quality score
163
+ - `VMAF_threshold`: Minimum required VMAF score
164
+ - `PIE-APP_Final_Score`: Normalized PIE-APP score (0-1 range)
165
+
166
+ #### Metric Integration
167
+
168
+ | Metric | Range | Primary Function | Usage |
169
+ |--------|-------|------------------|-------|
170
+ | **VMAF** | 0-100 | Threshold validation | Upscaling verification |
171
+ | **PIE-APP** | 0-1 (final) | Quality scoring | Performance evaluation |
172
+
173
+ ---
174
+
175
+ ### Content Length Scoring
176
+
177
+ #### Dynamic Content Length Requests
178
+
179
+ Miners actively request content processing durations within 35-second evaluation windows, enabling optimized resource allocation and performance assessment.
180
+
181
+ #### Available Processing Durations
182
+
183
+ | Duration | Status | Availability |
184
+ |----------|--------|--------------|
185
+ | 5s | ✅ Default | Currently Available |
186
+ | 10s | ✅ Available | Currently Available |
187
+ | 20s | 🔄 Coming Soon | Future Release |
188
+ | 40s | 🔄 Coming Soon | Future Release |
189
+ | 80s | 🔄 Coming Soon | Future Release |
190
+ | 160s | 🔄 Coming Soon | Future Release |
191
+ | 320s | 🔄 Coming Soon | Future Release |
192
+
193
+ > **Current Limitation**: Processing durations up to 10 seconds are currently supported.
194
+
195
+ #### Length Score Mathematical Model
196
+
197
+ **Formula:**
198
+ ```
199
+ S_L = log(1 + content_length) / log(1 + 320)
200
+ ```
201
+
202
+ **Parameters:**
203
+ - `content_length`: Processing duration in seconds
204
+ - `S_L`: Normalized length score (0 to 1)
205
+
206
+ #### Performance Analysis Table
207
+
208
+ | Duration (s) | S_L Score | Percentage | Improvement | Performance Tier |
209
+ |--------------|-----------|------------|-------------|------------------|
210
+ | 5 | 0.3105 | 31.05% | Baseline | Default |
211
+ | 10 | 0.4155 | 41.55% | +33% | Significant Gain |
212
+ | 20 | 0.5275 | 52.75% | +27% | Strong Performance |
213
+ | 40 | 0.6434 | 64.34% | +22% | High Capability |
214
+ | 80 | 0.7614 | 76.14% | +18% | Advanced Processing |
215
+ | 160 | 0.8804 | 88.04% | +16% | Expert Level |
216
+ | 320 | 1.0000 | 100.00% | +14% | Maximum Score |
217
+
218
+ #### Logarithmic Scaling Benefits
219
+
220
+ | Benefit | Description | Impact |
221
+ |---------|-------------|---------|
222
+ | **Fair Distribution** | Balanced scoring across duration ranges | Equitable competition |
223
+ | **Diminishing Returns** | Reduced gains for extreme durations | Prevents over-optimization |
224
+ | **Normalized Output** | Consistent 0-1 scoring range | Standardized evaluation |
225
+ | **Capacity Recognition** | Rewards longer processing capabilities | Incentivizes advancement |
226
+
227
+ **Strategic Insights:**
228
+ - **Optimal Entry Point**: 10s processing provides largest relative improvement (+33%)
229
+ - **Scaling Pattern**: Each duration doubling yields progressively smaller benefits
230
+ - **Maximum Achievement**: 320s processing represents theoretical performance ceiling
231
+
232
+ ---
233
+
234
+ ### Upscaling Final Score
235
+
236
+ #### Score Component Architecture
237
+
238
+ The comprehensive upscaling scoring system integrates two fundamental metrics:
239
+
240
+ | Component | Symbol | Description | Weight |
241
+ |-----------|--------|-------------|--------|
242
+ | **Quality Score** | S_Q | Processing accuracy and output quality | W1 = 0.5 |
243
+ | **Length Score** | S_L | Content processing capacity | W2 = 0.5 |
244
+
245
+ #### Preliminary Score Calculation
246
+
247
+ **Formula:**
248
+ ```
249
+ S_pre = S_Q × W1 + S_L × W2
250
+ ```
251
+
252
+ **Current Configuration:**
253
+ - `W1 = 0.5` (Quality weight)
254
+ - `W2 = 0.5` (Length weight)
255
+
256
+ > **Dynamic Adjustment**: Weights are continuously optimized based on real-world performance data and network requirements.
257
+
258
+ #### Final Score Transformation
259
+
260
+ The preliminary score undergoes exponential transformation for enhanced performance differentiation:
261
+
262
+ **Formula:**
263
+ ```
264
+ S_F = 0.1 × e^(6.979 × (S_pre - 0.5))
265
+ ```
266
+
267
+ **Parameters:**
268
+ - `S_F`: Final upscaling score
269
+ - `S_pre`: Preliminary combined score
270
+ - `e`: Euler's number (≈2.718)
271
+
272
+ #### Performance Tier Analysis
273
+
274
+ | S_pre | S_F Score | Multiplier | Performance Tier | Reward Category |
275
+ |-------|-----------|------------|------------------|-----------------|
276
+ | 0.30 | 0.0248 | 0.25× | Poor Performance | Significant Penalty |
277
+ | 0.36 | 0.0376 | 0.38× | Below Average | Moderate Penalty |
278
+ | 0.42 | 0.0572 | 0.57× | Low Average | Minor Penalty |
279
+ | 0.48 | 0.0870 | 0.87× | Near Average | Slight Penalty |
280
+ | 0.54 | 0.1322 | 1.32× | Above Average | Moderate Reward |
281
+ | 0.60 | 0.2010 | 2.01× | Good Performance | Strong Reward |
282
+ | 0.66 | 0.3055 | 3.05× | High Performance | Major Reward |
283
+ | 0.72 | 0.4643 | 4.64× | Very High Performance | Excellent Reward |
284
+ | 0.78 | 0.7058 | 7.06× | Excellent Performance | Outstanding Reward |
285
+ | 0.84 | 1.0728 | 10.73× | Outstanding Performance | Elite Reward |
286
+ | 0.90 | 1.6307 | 16.31× | Elite Performance | Maximum Reward |
287
+
288
+ #### Exponential Function Characteristics
289
+
290
+ **System Benefits:**
291
+ | Feature | Description | Strategic Impact |
292
+ |---------|-------------|------------------|
293
+ | **Enhanced Differentiation** | Clear performance tier separation | Competitive advantage clarity |
294
+ | **Reward Amplification** | 16× multiplier difference (top vs bottom) | Strong performance incentives |
295
+ | **Competitive Optimization** | Non-linear improvement rewards | Encourages continuous advancement |
296
+ | **Exponential Scaling** | Small S_pre gains yield large S_F improvements | High-performance focus |
297
+
298
+ **Strategic Performance Guidelines:**
299
+ - **Minimum Target**: Achieve S_pre > 0.6 for meaningful reward activation
300
+ - **Optimization Focus**: Exponential curve creates powerful excellence incentives
301
+ - **High-Performance Strategy**: Small quality improvements at elevated levels yield disproportionate benefits
302
+
303
+ #### Graph Analysis
304
+
305
+ !<img src="./images/graph3.png" alt="Length Score Analysis" width="1200" height="1000">
306
+
307
+ ---
308
+
309
+ ### Upscaling Penalty & Bonus System
310
+
311
+ #### Historical Performance Multiplier Architecture
312
+
313
+ The advanced upscaling scoring system incorporates a **rolling 10-round historical performance window** to evaluate consistency patterns and apply dynamic multipliers based on sustained performance trends.
314
+
315
+ #### System Formula
316
+
317
+ ```
318
+ Final Adjusted Score = S_F × Performance Multiplier
319
+ Performance Multiplier = Bonus Multiplier × S_F Penalty × S_Q Penalty
320
+ ```
321
+
322
+ ---
323
+
324
+ #### Bonus System (Excellence Rewards)
325
+
326
+ **Activation Criteria:** `S_F > 0.32` in mining round
327
+
328
+ **Mathematical Model:**
329
+ ```python
330
+ bonus_multiplier = 1.0 + (bonus_count / 10) × 0.15
331
+ ```
332
+
333
+ **System Characteristics:**
334
+ | Parameter | Value | Description |
335
+ |-----------|-------|-------------|
336
+ | **Maximum Bonus** | +15% | All 10 rounds achieve S_F > 0.32 |
337
+ | **Scaling Method** | Linear | Based on consistency frequency |
338
+ | **Primary Purpose** | Sustained excellence reward | Long-term performance incentive |
339
+
340
+ **Example Calculation:** 7/10 rounds with S_F > 0.32 → 1.105× multiplier (+10.5% bonus)
341
+
342
+ ---
343
+
344
+ #### S_F Penalty System (Performance Penalties)
345
+
346
+ **Activation Criteria:** `S_F < 0.20` in mining round
347
+
348
+ **Mathematical Model:**
349
+ ```python
350
+ penalty_f_multiplier = 1.0 - (penalty_f_count / 10) × 0.20
351
+ ```
352
+
353
+ **System Characteristics:**
354
+ | Parameter | Value | Description |
355
+ |-----------|-------|-------------|
356
+ | **Maximum Penalty** | -20% | All 10 rounds achieve S_F < 0.20 |
357
+ | **Scaling Method** | Linear | Based on poor performance frequency |
358
+ | **Primary Purpose** | Performance consistency enforcement | Discourages sustained poor results |
359
+
360
+ **Example Calculation:** 4/10 rounds with S_F < 0.20 → 0.92× multiplier (-8% penalty)
361
+
362
+ ---
363
+
364
+ #### S_Q Penalty System (Quality Penalties)
365
+
366
+ **Activation Criteria:** `S_Q < 0.25` in mining round
367
+
368
+ **Mathematical Model:**
369
+ ```python
370
+ penalty_q_multiplier = 1.0 - (penalty_q_count / 10) × 0.25
371
+ ```
372
+
373
+ **System Characteristics:**
374
+ | Parameter | Value | Description |
375
+ |-----------|-------|-------------|
376
+ | **Maximum Penalty** | -25% | All 10 rounds achieve S_Q < 0.25 |
377
+ | **Scaling Method** | Linear | Based on quality failure frequency |
378
+ | **Primary Purpose** | Quality standard enforcement | Strongest penalty (quality is critical) |
379
+
380
+ **Example Calculation:** 3/10 rounds with S_Q < 0.25 → 0.925× multiplier (-7.5% penalty)
381
+
382
+ ---
383
+
384
+ #### Performance Multiplier Case Studies
385
+
386
+ | Miner Category | Avg S_F | Avg S_Q | Bonus Rate | S_F Penalty | S_Q Penalty | **Final Multiplier** | **Net Effect** |
387
+ |----------------|---------|---------|------------|-------------|-------------|---------------------|----------------|
388
+ | **Elite Miner** | 0.854 | 0.717 | 10/10 | 0/10 | 0/10 | **1.150×** | **+15.0%** |
389
+ | **Good Miner** | 0.653 | 0.571 | 0/10 | 0/10 | 0/10 | **1.000×** | **±0.0%** |
390
+ | **Average Miner** | 0.511 | 0.505 | 0/10 | 0/10 | 3/10 | **0.925×** | **-7.5%** |
391
+ | **Poor Miner** | 0.311 | 0.411 | 0/10 | 10/10 | 10/10 | **0.600×** | **-40.0%** |
392
+
393
+ #### Penalty Analysis
394
+
395
+ !<img src="./images/graph4.png" alt="Length Score Analysis" width="1200" height="800">
396
+
397
+ ---
398
+
399
+ #### System Benefits & Strategic Impact
400
+
401
+ **Core System Benefits:**
402
+
403
+ | Benefit | Description | Strategic Impact |
404
+ |---------|-------------|------------------|
405
+ | 🎯 **Consistency Rewards** | Elite miners maintain sustained +15% bonus | Long-term competitive advantage |
406
+ | ⚡ **Responsive Penalties** | Poor performance accumulates immediate penalties | Rapid feedback mechanism |
407
+ | 🔄 **Recovery Incentive** | Miners can improve multipliers over 10 rounds | Encourages continuous improvement |
408
+ | ⚖️ **Balanced Impact** | Quality penalties are strongest (-25% max) | Emphasizes quality importance |
409
+ | 📈 **Progressive Scaling** | Linear scaling prevents extreme swings | Maintains system stability |
410
+
411
+ ---
412
+
413
+ ## Compression System
414
+
415
+ ### Compression Scoring
416
+
417
+ #### Task Parameters
418
+
419
+ Each compression task includes specific requirements:
420
+
421
+ | Parameter | Description | Range | Impact |
422
+ |-----------|-------------|-------|---------|
423
+ | **VMAF Threshold** | Minimum acceptable quality score | 0-100 | Quality validation |
424
+ | **Original File Size** | Baseline for compression calculation | Variable | Compression rate baseline |
425
+ | **Target Optimization** | Balance between compression and quality | Dynamic | Performance strategy |
426
+
427
+ #### Compression Rate Calculation
428
+
429
+ **Formula:**
430
+ ```
431
+ C = compressed_file_size / original_file_size
432
+ ```
433
+
434
+ **Where:**
435
+ - `C`: Compression rate (0 < C ≤ 1)
436
+ - `compressed_file_size`: Size of processed video file
437
+ - `original_file_size`: Size of original video file
438
+
439
+ **Characteristics:**
440
+ - **Lower C values** indicate better compression (smaller files)
441
+ - **C = 1.0** means no compression achieved
442
+ - **C < 1.0** indicates successful compression
443
+
444
+ #### VMAF Quality Assessment
445
+
446
+ **Implementation:**
447
+ ```
448
+ VMAF_score = Harmonic_Mean(VMAF_frame_1, VMAF_frame_2, ..., VMAF_frame_n)
449
+ ```
450
+
451
+ **Where:**
452
+ - `VMAF_frame_i`: VMAF score for frame `i`
453
+ - `n`: Number of sampled frames (5 random frames)
454
+ - `Harmonic_Mean`: Emphasizes poor-quality frame impact
455
+
456
+ #### Final Compression Score Calculation
457
+
458
+ **Threshold Validation:**
459
+ ```
460
+ If VMAF_score < VMAF_threshold:
461
+ S_f = 0 (Zero score for quality violation)
462
+ Else:
463
+ S_f = w_c × (1 - C^1.5) + w_vmaf × (VMAF_score - VMAF_threshold) / (100 - VMAF_threshold)
464
+ ```
465
+
466
+ **Parameters:**
467
+ - `S_f`: Final compression score
468
+ - `w_c`: Weight for compression rate (default: 0.8)
469
+ - `w_vmaf`: Weight for VMAF score (default: 0.2)
470
+ - `C`: Compression rate
471
+ - `VMAF_score`: Achieved VMAF quality score
472
+ - `VMAF_threshold`: Minimum required VMAF score
473
+
474
+ #### Mathematical Properties
475
+
476
+ **Compression Rate Component:**
477
+ - **Formula**: `w_c × (1 - C^1.5)`
478
+ - **Range**: [0, w_c] (0 when C = 1, w_c when C = 0)
479
+ - **Curve**: Concave function emphasizing compression efficiency
480
+ - **Exponent 1.5**: Provides balanced reward for compression achievements
481
+
482
+ **VMAF Quality Component:**
483
+ - **Formula**: `w_vmaf × (VMAF_score - VMAF_threshold) / (100 - VMAF_threshold)`
484
+ - **Range**: [0, w_vmaf] (0 at threshold, w_vmaf at maximum quality)
485
+ - **Normalization**: Scales quality improvement relative to achievable range
486
+ - **Linear scaling**: Direct correlation between quality improvement and score
487
+
488
+ #### Graph Analysis
489
+
490
+ !<img src="./images/graph5.png" alt="Scoring impact" width="1200" height="600">
491
+
492
+ ---
493
+
494
+ #### Performance Analysis Examples
495
+
496
+ | Scenario | C | VMAF_score | VMAF_threshold | S_f | Performance Tier |
497
+ |----------|---|------------|----------------|-----|------------------|
498
+ | **Excellent** | 0.3 | 85 | 70 | 0.669 + 0.100 = **0.769** | Outstanding |
499
+ | **Good** | 0.5 | 80 | 70 | 0.517 + 0.067 = **0.584** | Strong |
500
+ | **Average** | 0.7 | 75 | 70 | 0.331 + 0.033 = **0.364** | Acceptable |
501
+ | **Poor Quality** | 0.4 | 65 | 70 | **0.000** | Failed |
502
+ | **No Compression** | 1.0 | 90 | 70 | 0.000 + 0.133 = **0.133** | Inefficient |
503
+
504
+ #### Strategic Guidelines
505
+
506
+ | Miner Strategy | Focus Area | Target Metrics | Expected Outcome |
507
+ |----------------|------------|---------------|------------------|
508
+ | **Quality-First** | Maintain high VMAF scores | VMAF_score >> VMAF_threshold | Consistent moderate scores |
509
+ | **Compression-First** | Maximize file size reduction | C << 1.0 | Variable scores based on quality |
510
+ | **Balanced Approach** | Optimize both factors | Moderate C + Good VMAF | Optimal long-term performance |
511
+ | **Threshold Gaming** | Minimal quality compliance | VMAF_score ≈ VMAF_threshold | Low scores, high risk |
512
+
513
+ ---
514
+
515
+ ### Compression Penalty & Bonus System
516
+
517
+ #### Historical Performance Multiplier Architecture
518
+
519
+ The compression scoring system incorporates a **rolling 10-round historical performance window** to evaluate consistency patterns and apply dynamic multipliers based on sustained performance trends.
520
+
521
+ #### System Formula
522
+
523
+ ```
524
+ Final Adjusted Compression Score = S_f × Performance Multiplier
525
+ Performance Multiplier = Bonus Multiplier × S_f Penalty × VMAF Penalty
526
+ ```
527
+
528
+ ---
529
+
530
+ #### Bonus System (Excellence Rewards)
531
+
532
+ **Activation Criteria:** `S_f > 0.74` in compression mining round
533
+
534
+ **Mathematical Model:**
535
+ ```python
536
+ bonus_multiplier = 1.0 + (bonus_count / 10) × 0.15
537
+ ```
538
+
539
+ **System Characteristics:**
540
+ | Parameter | Value | Description |
541
+ |-----------|-------|-------------|
542
+ | **Maximum Bonus** | +15% | All 10 rounds achieve S_f > 0.74 |
543
+ | **Scaling Method** | Linear | Based on consistency frequency |
544
+ | **Primary Purpose** | Sustained excellence reward | Long-term performance incentive |
545
+
546
+ **Example Calculation:** 7/10 rounds with S_f > 0.74 → 1.105× multiplier (+10.5% bonus)
547
+
548
+ ---
549
+
550
+ #### S_f Penalty System (Performance Penalties)
551
+
552
+ **Activation Criteria:** `S_f < 0.4` in compression mining round
553
+
554
+ **Mathematical Model:**
555
+ ```python
556
+ penalty_f_multiplier = 1.0 - (penalty_f_count / 10) × 0.20
557
+ ```
558
+
559
+ **System Characteristics:**
560
+ | Parameter | Value | Description |
561
+ |-----------|-------|-------------|
562
+ | **Maximum Penalty** | -20% | All 10 rounds achieve S_f < 0.4 |
563
+ | **Scaling Method** | Linear | Based on poor performance frequency |
564
+ | **Primary Purpose** | Performance consistency enforcement | Discourages sustained poor results |
565
+
566
+ **Example Calculation:** 4/10 rounds with S_f < 0.4 → 0.92× multiplier (-8% penalty)
567
+
568
+ ---
569
+
570
+ #### Compression Performance Multiplier Case Studies
571
+
572
+ | Miner Category | Avg S_f | Avg VMAF Margin | Bonus Rate | S_f Penalty | **Final Multiplier** | **Net Effect** |
573
+ |----------------|---------|-----------------|------------|-------------|---------------|---------------------|----------------|
574
+ | **Elite Compressor** | 0.654 | +15 | 10/10 | 0/10 | **1.150×** | **+15.0%** |
575
+ | **Good Compressor** | 0.453 | +8 | 0/10 | 0/10 | **1.000×** | **±0.0%** |
576
+ | **Average Compressor** | 0.311 | +3 | 0/10 | 0/10 | **0.910×** | **-9.0%** |
577
+ | **Poor Compressor** | 0.211 | -2 | 0/10 | 10/10 | **0.490×** | **-51.0%** |
578
+
579
+ #### Compression Penalty Analysis
580
+
581
+ **Strategic Impact:**
582
+ - **Quality-first approach** strongly incentivized through higher penalties
583
+ - **Consistency rewards** for miners maintaining quality above threshold
584
+ - **Immediate feedback** through zero-score for quality violations
585
+ - **Balanced optimization** encouraged through dual-factor scoring
586
+
587
+ ---
588
+
589
+ ## Implementation Guidelines
590
+
591
+ ### Performance Monitoring
592
+
593
+ **Real-time Operations:**
594
+ - ✅ Scores calculated in real-time during mining operations
595
+ - ✅ Historical performance data maintained for trend analysis and multiplier calculation
596
+ - ✅ Weight adjustments implemented based on network-wide performance metrics
597
+ - ✅ Performance multipliers updated after each mining round
598
+
599
+ ### Scoring Strategy Recommendations
600
+
601
+ **Upscaling Performance-Based Guidelines:**
602
+
603
+ | Miner Category | Primary Focus | Strategic Recommendations |
604
+ |----------------|---------------|---------------------------|
605
+ | **New Miners** | Foundation Building | Focus on achieving consistent S_pre > 0.5 before optimizing for length |
606
+ | **Established Miners** | Quality Optimization | Prioritize quality improvements when S_pre > 0.6 to avoid S_Q penalties |
607
+ | **Elite Miners** | Consistency Maintenance | Maintain consistency above S_F > 0.32 to secure maximum bonus multipliers |
608
+ | **Recovery Phase** | Systematic Improvement | Focus on quality (S_Q > 0.25) first, then performance (S_F > 0.20) to restore multipliers |
609
+
610
+ **Compression Performance-Based Guidelines:**
611
+
612
+ | Miner Category | Primary Focus | Strategic Recommendations |
613
+ |----------------|---------------|---------------------------|
614
+ | **New Compressors** | Quality Compliance | Focus on maintaining VMAF_score > VMAF_threshold + 5 |
615
+ | **Established Compressors** | Balanced Optimization | Optimize both compression rate and quality maintenance |
616
+ | **Elite Compressors** | Consistency Excellence | Maintain S_f > 0.74 consistently for bonus multipliers |
617
+ | **Recovery Phase** | Quality Restoration | Focus on VMAF compliance first, then compression optimization |
618
+
619
+ ### Future Enhancement Roadmap
620
+
621
+ **Planned Developments:**
622
+
623
+ - [ ] **Extended Content Length Support** - Processing durations up to 320s
624
+ - [ ] **Dynamic Weight Adjustment Algorithms** - Automated optimization based on network performance
625
+ - [ ] **Advanced Quality Metrics Integration** - Additional assessment parameters
626
+ - [ ] **Multi-dimensional Scoring Parameters** - Enhanced evaluation criteria
627
+ - [ ] **Adaptive Difficulty Scaling** - Network performance-based adjustments
628
+ - [ ] **Advanced Penalty/Bonus Optimization** - Network-wide performance distribution analysis
629
+ - [ ] **Seasonal Performance Multiplier Adjustments** - Time-based optimization cycles
630
+ - [ ] **Cross-Task Performance Integration** - Unified scoring across upscaling and compression
631
+
632
+ ---
633
+
634
+ ## Technical Specifications
635
+
636
+ ### Core System Parameters
637
+
638
+ | Parameter | Current Value | Configurable Range | Implementation Notes |
639
+ |-----------|---------------|-------------------|---------------------|
640
+ | **Default Content Length** | 5s | 5s - 10s | Actively configurable by miners |
641
+ | **Quality Weight (W1)** | 0.5 | 0.0 - 1.0 | Dynamically adjusted based on network data |
642
+ | **Length Weight (W2)** | 0.5 | 0.0 - 1.0 | Dynamically adjusted based on network data |
643
+
644
+ ### Compression System Parameters
645
+
646
+ | Parameter | Current Value | Configurable Range | Implementation Notes |
647
+ |-----------|---------------|-------------------|---------------------|
648
+ | **Compression Rate Weight (w_c)** | 0.8 | 0.6 - 0.9 | Balances compression efficiency vs quality |
649
+ | **VMAF Score Weight (w_vmaf)** | 0.2 | 0.1 - 0.4 | Balances quality maintenance vs compression |
650
+ | **Compression Rate Exponent** | 1.5 | 1.2 - 2.0 | Controls compression reward curve steepness |
651
+ | **VMAF Safety Margin** | +5 | +3 - +10 | Quality buffer above threshold |
652
+ | **Zero-Score Threshold** | VMAF_score < VMAF_threshold | Fixed | Immediate penalty for quality violations |
653
+
654
+ ### Performance Multiplier System Parameters
655
+
656
+ | Parameter | Current Value | Configurable Range | System Impact |
657
+ |-----------|---------------|-------------------|---------------|
658
+ | **Performance History Window** | 10 rounds | 5-10 rounds | Configurable for different network conditions |
659
+ | **Upscaling Bonus Threshold** | S_F > 0.32 | 0.3-0.4 | Adjustable based on network performance |
660
+ | **Upscaling S_F Penalty Threshold** | S_F < 0.20 | 0.15-0.25 | Adjustable based on network performance |
661
+ | **Upscaling S_Q Penalty Threshold** | S_Q < 0.25 | 0.2-0.3 | Adjustable based on network performance |
662
+ | **Compression Bonus Threshold** | S_f > 0.74 | 0.7-0.8 | Lower threshold reflecting compression difficulty |
663
+ | **Compression S_f Penalty Threshold** | S_f < 0.4 | 0.35-0.45 | Penalty for poor compression performance |
664
+ | **VMAF Penalty Threshold** | VMAF_score < VMAF_threshold + 5 | +3 to +10 | Quality safety margin enforcement |
665
+ | **Maximum Bonus** | +15% | 10%-20% | Scalable reward system |
666
+ | **Maximum S_F Penalty** | -20% | 15%-25% | Scalable penalty system |
667
+ | **Maximum S_Q Penalty** | -25% | 20%-30% | Strongest penalty for quality issues |
668
+
669
+ ---
670
+
671
+ ## Mathematical Properties
672
+
673
+ ### Upscaling Length Score Function Properties
674
+
675
+ **Mathematical Characteristics:**
676
+ - **Domain**: [5, 320] seconds
677
+ - **Range**: [0.3105, 1.0000]
678
+ - **Function Type**: Logarithmic (concave)
679
+ - **Growth Rate**: Decreasing marginal returns
680
+ - **Optimization Point**: Balanced between processing capability and diminishing returns
681
+
682
+ ### Upscaling Final Score Function Properties
683
+
684
+ **Mathematical Characteristics:**
685
+ - **Domain**: [0, 1] (S_pre values)
686
+ - **Range**: [0.0025, 40.43] (theoretical maximum)
687
+ - **Function Type**: Exponential (convex)
688
+ - **Critical Point**: S_pre = 0.5 (inflection point for reward/penalty)
689
+ - **Scaling Behavior**: Exponential amplification of performance differences
690
+
691
+ ### Compression Score Function Properties
692
+
693
+ **Mathematical Characteristics:**
694
+ - **Domain**: [0, 1] (compression rate C values)
695
+ - **Range**: [0, w_c] (compression component score)
696
+ - **Function Type**: Concave (1 - C^1.5)
697
+ - **Critical Point**: C = 1 (no compression = zero score)
698
+ - **Optimization**: Lower C values yield higher scores with diminishing returns
699
+
700
+ ### Compression VMAF Component Properties
701
+
702
+ **Mathematical Characteristics:**
703
+ - **Domain**: [VMAF_threshold, 100] (achievable quality range)
704
+ - **Range**: [0, w_vmaf] (quality component score)
705
+ - **Function Type**: Linear normalization
706
+ - **Zero Point**: VMAF_score = VMAF_threshold
707
+ - **Maximum Point**: VMAF_score = 100
708
+ - **Quality Buffer**: +5 margin for penalty system
709
+
710
+ ### Performance Multiplier Properties
711
+
712
+ **Mathematical Characteristics:**
713
+ - **Domain**: [0.55, 1.15] (practical operational range)
714
+ - **Function Type**: Linear combination of historical performance frequencies
715
+ - **Update Frequency**: After each mining round completion
716
+ - **Memory System**: Rolling 10-round window with automatic history management
717
+ - **Convergence**: Stabilizes after 10 rounds of consistent performance patterns
718
+
719
+ ---
720
+
721
+ ## Conclusion
722
+
723
+ The VIDAIO subnet validation and incentive mechanism represents a comprehensive, mathematically-grounded approach to ensuring high-quality video processing while maintaining fair competition and encouraging continuous improvement. Through the integration of industry-standard metrics (VMAF and PIE-APP), dynamic scoring systems, and sophisticated penalty/bonus mechanisms, the system creates a robust environment that rewards excellence and consistency while providing clear pathways for improvement.
724
+
725
+ The system now supports both **upscaling** and **compression** tasks, each with specialized scoring mechanisms:
726
+ - **Upscaling tasks** focus on quality improvement using PIE-APP scoring with VMAF threshold validation
727
+ - **Compression tasks** balance file size reduction with quality maintenance using dual-factor scoring
728
+ - **Unified penalty systems** ensure consistent quality standards across all task types
729
+
730
+ These metrics together ensure that miners maintain high-quality video processing standards while meeting demands for fast and efficient processing, creating a sustainable and competitive ecosystem for video enhancement and optimization services.
731
+
732
+ ---
733
+
734
+ *This documentation is continuously updated to reflect the latest scoring mechanisms, performance optimizations, and system enhancements.*
docs/miner_setup.md ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Running Miner
2
+
3
+ A high-performance decentralized video processing miner that leverages **Video2X** for AI-powered video upscaling. This guide provides detailed instructions to set up, configure, and run the miner effectively
4
+
5
+ ---
6
+
7
+ ## Machine Requirements
8
+
9
+ To achieve optimal results, we recommend the following setup:
10
+
11
+ - **Operating System**: Ubuntu 24.04 LTS or higher
12
+ [Learn more about Ubuntu 24.04 LTS](https://ubuntu.com/blog/tag/ubuntu-24-04-lts)
13
+ - **GPU**: NVIDIA RTX 4090 or higher
14
+ (Required for efficient video upscaling using Video2X)
15
+
16
+ ---
17
+
18
+ ## Install PM2 (Process Manager)
19
+
20
+ **PM2** is used to manage and monitor the miner process. If you haven’t installed PM2 yet, follow these steps:
21
+
22
+ 1. Install `npm` and PM2:
23
+ ```bash
24
+ sudo apt update
25
+ sudo apt install npm -y
26
+ sudo npm install pm2 -g
27
+ pm2 update
28
+ ```
29
+
30
+ 2. For more details, refer to the [PM2 Documentation](https://pm2.io/docs/runtime/guide/installation/).
31
+
32
+ ---
33
+
34
+ ## Install Redis
35
+
36
+ 1. Install 'redis'
37
+ ```bash
38
+ sudo apt update
39
+ sudo apt install redis-server
40
+ sudo systemctl start redis
41
+ sudo systemctl enable redis-server
42
+ sudo systemctl status redis
43
+ ```
44
+
45
+ ## Install Project Dependencies
46
+
47
+ ### Prerequisites
48
+
49
+ - **Python**: Version 3.10 or higher
50
+ - **pip**: Python package manager
51
+ - **virtualenv** (optional): For dependency isolation
52
+
53
+ ---
54
+
55
+ ### 1. Clone the Repository
56
+
57
+ Clone the project repository to your local machine:
58
+ ```bash
59
+ git clone https://github.com/vidaio-subnet/vidaio-subnet.git
60
+ cd vidaio-subnet
61
+ ```
62
+
63
+ ---
64
+
65
+ ### 2. Set Up a Virtual Environment (Recommended)
66
+
67
+ Create and activate a virtual environment to isolate project dependencies:
68
+ ```bash
69
+ python3 -m venv venv
70
+ source venv/bin/activate
71
+ ```
72
+
73
+ ---
74
+
75
+ ### 3. Install the Package and Dependencies
76
+
77
+ Install the project and its dependencies using `pip`:
78
+ ```bash
79
+ pip install -e .
80
+ ```
81
+
82
+ ---
83
+
84
+ ### 4. Configure Environment Variables
85
+
86
+ To configure environment variables, follow these steps:
87
+
88
+ 1. Create a `.env` file in the project root directory by referencing the provided `.env.template` file:
89
+ ```bash
90
+ cp .env.template .env
91
+ ```
92
+
93
+ 2. Set up a bucket in cloud storage. The base miner code utilizes MinIO to connect with cloud storage services, so you'll need to prepare your bucket using a platform that supports MinIO integration, such as Backblaze. Alternatively, you can modify the code to suit your specific requirements.
94
+ 3. Add the required variables to the `.env` file. For example:
95
+ ```env
96
+ BUCKET_NAME="S3 buckent name"
97
+ BUCKET_COMPATIBLE_ENDPOINT="S3 bucket endpoint"
98
+ BUTKET_COMPATIBLE_ACCESS_KEY="S3 bucket personal access key"
99
+ BUCKET_COMPATIBLE_SECRET_KEY="S3 bucket personal secret key"
100
+ PEXELS_API_KEY="Your Pexels account api key"
101
+ WANDB_API_KEY="Your WANDB account api key"
102
+ ```
103
+
104
+ 4. Ensure that the bucket is configured with the appropriate permissions to allow file uploads and enable public access for downloads via presigned URLs.
105
+
106
+ 5. Once the `.env` file is properly configured, the application will use the specified credentials for S3 bucket and Pexels.
107
+
108
+
109
+ ---
110
+
111
+ ## Install Video2X
112
+
113
+ The miner requires **Video2X** for AI-powered video upscaling. Follow the steps below to install and configure Video2X.
114
+
115
+ ---
116
+
117
+ ### Step 1: Install FFMPEG
118
+
119
+ FFMPEG is required for processing video files. Install it using the following commands:
120
+ ```bash
121
+ sudo apt update
122
+ sudo apt install ffmpeg -y
123
+ ```
124
+
125
+ For more details, refer to the [FFMPEG Documentation](https://www.ffmpeg.org/download.html#build-linux).
126
+
127
+ ---
128
+
129
+ ### Step 2: Install CUDA and NVCC
130
+
131
+ Ensure your CUDA drivers and NVCC (NVIDIA Compiler) are properly installed and configured to support GPU acceleration.
132
+
133
+ 1. Verify your CUDA installation:
134
+ ```bash
135
+ nvcc --version
136
+ ```
137
+
138
+ 2. Ensure you CUDA driver is installed correctly
139
+ <!-- Install or update the CUDA drivers if they are not already installed:
140
+ ```bash
141
+ sudo apt update
142
+ sudo apt install nvidia-cuda-toolkit -y
143
+ ``` -->
144
+
145
+ For more information, refer to the [CUDA Toolkit Installation Guide](https://developer.nvidia.com/cuda-toolkit).
146
+
147
+ ---
148
+
149
+ ### Step 3: Install Video2X
150
+
151
+ ---
152
+
153
+ ### Option 1: Install Video2X by downloading Debian Package:
154
+
155
+
156
+ 1. **Download the Video2X `.deb` package**:
157
+ ```bash
158
+ wget -P services/upscaling/models https://github.com/k4yt3x/video2x/releases/download/6.3.1/video2x-linux-ubuntu2404-amd64.deb
159
+ ```
160
+
161
+ 2. **Install the package using `dpkg`**:
162
+ ```bash
163
+ sudo dpkg -i services/upscaling/models/video2x-linux-ubuntu2404-amd64.deb
164
+ ```
165
+
166
+ 3. **Resolve dependencies** (if any):
167
+ ```bash
168
+ sudo apt-get install -f
169
+ ```
170
+
171
+ ### Option 2: Install Video2X by Building Debian Package:
172
+
173
+ 1. Install Cargo
174
+ Cargo is required to build the Video2X package:
175
+ ```bash
176
+ sudo apt-get update
177
+ sudo apt-get install cargo -y
178
+ cargo install just --version=1.39.0
179
+ ```
180
+
181
+ 2. Clone the Video2X Repository
182
+ you can clone this repository within the current vidaio-subnet package
183
+ ```bash
184
+ git clone --recurse-submodules https://github.com/vidAio-subnet/video2x
185
+ cd video2x
186
+ ```
187
+
188
+ 3. Build the Video2X Project
189
+ Before building, ensure `~/.cargo/bin` is included in your `PATH` environment variable:
190
+ ```bash
191
+ export PATH="$HOME/.cargo/bin:$PATH"
192
+ ```
193
+
194
+ Run the following command to build the package:
195
+ ```bash
196
+ just ubuntu2404
197
+ ```
198
+
199
+ Once the build is complete, the `.deb` package will be located in the current directory.
200
+
201
+ 4. Install the Built Package
202
+ Install the `.deb` package using:
203
+ ```bash
204
+ sudo dpkg -i video2x-linux-ubuntu-amd64.deb
205
+ ```
206
+
207
+ ---
208
+ For additional details, refer to the [Video2X Documentation](https://docs.video2x.org/building/linux.html).
209
+
210
+ ## Running the Video Upscaling Endpoint
211
+
212
+ You can run the video upscaling endpoint using **PM2** to manage the process:
213
+
214
+ ```bash
215
+ pm2 start "python services/upscaling/server.py" --name video-upscaler
216
+ ```
217
+
218
+ ### Notes:
219
+ - The `video-upscaler` process will handle video upscaling requests.
220
+ - Use the following PM2 commands to manage the process:
221
+ - **View Logs**: `pm2 logs video-upscaler`
222
+ - **Restart**: `pm2 restart video-upscaler`
223
+ - **Stop**: `pm2 stop video-upscaler`
224
+
225
+ ---
226
+
227
+ ## Running the Video Compression Endpoint
228
+
229
+ You can also run the video Compression endpoint using **PM2** to manage the process:
230
+
231
+ ```bash
232
+ pm2 start "python services/compress/server.py" --name video-compressor
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Running the file deletion process
238
+
239
+ You can run the file deletion process using **PM2** to manage the process:
240
+
241
+ ```bash
242
+ pm2 start "python services/miner_utilities/file_deletion_server.py" --name video-deleter
243
+ ```
244
+
245
+ ## Running the Miner with PM2
246
+
247
+ To run the miner, use the following command:
248
+
249
+ ```bash
250
+ pm2 start "python3 neurons/miner.py --wallet.name [Your_Wallet_Name] --wallet.hotkey [Your_Hotkey_Name] --subtensor.network finney --netuid 85 --axon.port [port] --logging.debug" --name video-miner
251
+ ```
252
+
253
+ ### Parameters:
254
+ - **`--wallet.name`**: Replace `[Your_Wallet_Name]` with your wallet name.
255
+ - **`--wallet.hotkey`**: Replace `[Your_Hotkey_Name]` with your hotkey name.
256
+ - **`--subtensor.network`**: Specify the target network (e.g., `finney`).
257
+ - **`--netuid`**: Specify the network UID (e.g., `292`).
258
+ - **`--axon.port`**: Replace `[port]` with the desired port number.
259
+ - **`--logging.debug`**: Enables debug-level logging for detailed output.
260
+
261
+ ### Managing the Miner Process:
262
+ - **Start the Miner**: The above command will start the miner as a PM2 process named `video-miner`.
263
+ - **View Logs**: Use `pm2 logs video-miner` to monitor miner logs in real time.
264
+ - **Restart the Miner**: Use `pm2 restart video-miner` to restart the process.
265
+ - **Stop the Miner**: Use `pm2 stop video-miner` to stop the process.
266
+
267
+ ---
268
+
269
+ ## Additional Notes
270
+
271
+ - Ensure all dependencies are installed and configured correctly before running the miner.
272
+ - Use a high-performance GPU and sufficient system resources for optimal performance.
273
+ - For troubleshooting and debugging, refer to the logs available in PM2.
docs/validator_setup.md ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Running Validator
2
+
3
+ ---
4
+
5
+ ## Machine Requirements
6
+
7
+ To achieve optimal results, we recommend the following setup:
8
+
9
+ - **Operating System**: Ubuntu 24.04 LTS or higher
10
+ [Learn more about Ubuntu 24.04 LTS](https://ubuntu.com/blog/tag/ubuntu-24-04-lts)
11
+ - **GPU**: NVIDIA RTX A6000 with 48GB VRAM and 30 CPU Cores
12
+
13
+ ---
14
+
15
+ ## Install PM2 (Process Manager)
16
+
17
+ **PM2** is used to manage and monitor the validator process. If you haven’t installed PM2 yet, follow these steps:
18
+
19
+ 1. Install `npm` and PM2:
20
+ ```bash
21
+ sudo apt update
22
+ sudo apt install npm -y
23
+ sudo npm install pm2 -g
24
+ pm2 update
25
+ ```
26
+
27
+ 2. For more details, refer to the [PM2 Documentation](https://pm2.io/docs/runtime/guide/installation/).
28
+
29
+ ---
30
+
31
+ ## Install Redis
32
+
33
+ 1. Install 'redis'
34
+ ```bash
35
+ sudo apt update
36
+ sudo apt install redis-server
37
+ sudo systemctl start redis
38
+ sudo systemctl enable redis-server
39
+ sudo systemctl status redis
40
+ ```
41
+
42
+
43
+ ## Install Project Dependencies
44
+
45
+ ### Prerequisites
46
+
47
+ - **Python**: Version 3.10 or higher
48
+ - **pip**: Python package manager
49
+ - **virtualenv** (optional): For dependency isolation
50
+
51
+ ---
52
+
53
+ ### 1. Clone the Repository
54
+
55
+ Clone the project repository to your local machine:
56
+ ```bash
57
+ git clone https://github.com/vidaio-subnet/vidaio-subnet.git
58
+ cd vidaio-subnet
59
+ ```
60
+
61
+ ---
62
+
63
+ ### 2. Set Up a Virtual Environment (Recommended)
64
+
65
+ Create and activate a virtual environment to isolate project dependencies:
66
+ ```bash
67
+ python3 -m venv venv
68
+ source venv/bin/activate
69
+ ```
70
+
71
+ ---
72
+
73
+ ### 3. Install the Package and Dependencies
74
+
75
+ Install the project and its dependencies using `pip`:
76
+ ```bash
77
+ pip install -e .
78
+ ```
79
+
80
+ ---
81
+
82
+ ### 4. Configure Environment Variables
83
+
84
+ To configure environment variables, follow these steps:
85
+
86
+ 1. Create a `.env` file in the project root directory by referencing the provided `.env.template` file:
87
+ ```bash
88
+ cp .env.template .env
89
+ ```
90
+ 2. Set up a bucket in cloud storage. The base miner code utilizes MinIO to connect with cloud storage services, so you'll need to prepare your bucket using a platform that supports MinIO integration, such as Backblaze. Alternatively, you can modify the code to suit your specific requirements. *IMPORTANT*: Note that currently the `region` of the storage is hardcoded, and must be adjusted in `vidaio_subnet_core/utilities/storage_client.py` for corresponding storage, such as AWS.
91
+ 3. Add the required variables to the `.env` file. For example:
92
+ ```env
93
+ BUCKET_NAME="S3 buckent name"
94
+ BUCKET_COMPATIBLE_ENDPOINT="S3 bucket endpoint"
95
+ BUTKET_COMPATIBLE_ACCESS_KEY="S3 bucket personal access key"
96
+ BUCKET_COMPATIBLE_SECRET_KEY="S3 bucket personal secret key"
97
+ PEXELS_API_KEY="Your Pexels account api key"
98
+ WANDB_API_KEY="Your WANDB account api key"
99
+ ```
100
+
101
+ 4. Ensure that the bucket is configured with the appropriate permissions to allow file uploads and enable public access for downloads via presigned URLs.
102
+
103
+ 5. Create your Pexels API key and replace it. (https://www.pexels.com/)
104
+ 6. Once the `.env` file is properly configured, the application will use the specified credentials for S3 bucket, Pexels and Wandb.
105
+
106
+
107
+ ---
108
+
109
+ ## Install FFMPEG
110
+
111
+ FFMPEG is required for processing video files. Install it using the following commands:
112
+ ```bash
113
+ sudo apt update
114
+ sudo apt install ffmpeg -y
115
+ ```
116
+
117
+ For more details, refer to the [FFMPEG Documentation](https://www.ffmpeg.org/download.html#build-linux).
118
+
119
+ ---
120
+
121
+ ## Install VMAF
122
+
123
+ To enable video quality validation, install **VMAF** by following the steps below to set up a clean virtual environment, install dependencies, and compile the tool.
124
+
125
+ ---
126
+
127
+ Clone the VMAF repository into the working root directory of your `vidaio-subnet` package. If the `vidaio-subnet` virtual environment is currently active, deactivate it first:
128
+
129
+ ```bash
130
+ git clone https://github.com/vidAio-subnet/vmaf.git
131
+ cd vmaf
132
+ ```
133
+
134
+ ---
135
+
136
+ ### Step 1: Set Up a Virtual Environment in VMAF directory
137
+
138
+ 1. Install `venv` if it’s not already installed:
139
+ ```bash
140
+ python3 -m venv vmaf-venv
141
+ ```
142
+
143
+ 2. Activate the virtual environment:
144
+ ```bash
145
+ source vmaf-venv/bin/activate
146
+ ```
147
+
148
+ ---
149
+
150
+ ### Step 2: Install Dependencies
151
+
152
+ 1. Install `meson`:
153
+ ```bash
154
+ pip install meson
155
+ ```
156
+
157
+ 2. Install system dependencies:
158
+ ```bash
159
+ sudo apt-get update
160
+ sudo apt-get install nasm ninja-build doxygen xxd
161
+ ```
162
+ For Ninja, verify whether the package name is `ninja` or `ninja-build` before running the install command.
163
+
164
+ ---
165
+
166
+ ### Step 3: Compile VMAF
167
+
168
+
169
+ 1. Set up the build environment:
170
+ ```bash
171
+ cd libvmaf
172
+ meson setup build --buildtype release -Denable_avx512=true
173
+ ```
174
+
175
+ 2. Optional flags:
176
+ - Use `-Denable_float=true` to enable floating-point feature extractors.
177
+ - Use `-Denable_avx512=true` to enable AVX-512 SIMD instructions for faster processing on supported CPUs.
178
+ - Use `-Denable_cuda=true` to build with CUDA support (requires `nvcc` and CUDA >= 11).
179
+ **Note:** To enable CUDA successfully, ensure `nvcc` and the CUDA driver are installed. Refer to the [CUDA and NVCC setup guide](miner_setup.md#step-2-install-cuda-and-nvcc).
180
+ - Use `-Denable_nvtx=true` to enable NVTX marker support for profiling with Nsight Systems.
181
+ - **Recommendation:**
182
+ We recommend adding `-Denable_avx512=true` to enhance validation speed. If CUDA is available, include the flag `-Denable_cuda=true` But At present, VMAF does not include support for CUDA integration.
183
+
184
+ 3. Build the project:
185
+ ```bash
186
+ ninja -vC build
187
+ ```
188
+
189
+ ---
190
+
191
+ ### Step 4: Test the Build
192
+
193
+ Run tests to verify the build:
194
+ ```bash
195
+ ninja -vC build test
196
+ ```
197
+
198
+ ---
199
+
200
+ ### Step 5: Install VMAF
201
+
202
+ Install the library, headers, and the command-line tool:
203
+ ```bash
204
+ ninja -vC build install
205
+ ```
206
+
207
+ ---
208
+
209
+ ### Step 6: Generate Documentation
210
+
211
+ Generate HTML documentation:
212
+ ```bash
213
+ ninja -vC build doc/html
214
+ ```
215
+
216
+ ### Step 7: Deactivate vmaf-venv, activate project venv
217
+
218
+ ```bash
219
+ deactivate
220
+ cd ..
221
+ cd ..
222
+ source venv/bin/activate
223
+ ```
224
+
225
+ ## Running the Validator with PM2
226
+
227
+ To run the validator, use the following command:
228
+
229
+ ```bash
230
+ pm2 start run.sh --name vidaio_v_autoupdater -- --wallet.name [Your_Wallet_Name] --wallet.hotkey [Your_Hotkey_Name] --subtensor.network finney --netuid 85 --axon.port [port] --logging.debug
231
+ ```
232
+
233
+ ### Parameters:
234
+ - **`--wallet.name`**: Replace `[Your_Wallet_Name]` with your wallet name.
235
+ - **`--wallet.hotkey`**: Replace `[Your_Hotkey_Name]` with your hotkey name.
236
+ - **`--subtensor.network`**: Specify the target network (e.g., `finney`).
237
+ - **`--netuid`**: Specify the network UID (e.g., `85`).
238
+ - **`--axon.port`**: Replace `[port]` with the desired port number.
239
+ - **`--logging.debug`**: Enables debug-level logging for detailed output.
240
+
241
+ ---
iqf_scores.csv ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ file_name,input_size,output_size,final_compression,final_score,psnr_values,scene_type,luma_qp,metrics_resolution_width,metrics_resolution_height,metrics_frame_rate,quality_noise_level,quality_sharpness,quality_contrast,quality_brightness,quality_color_saturation,quality_motion_blur,quality_compression_artifacts,quality_text_content,quality_edge_density,quality_temporal_consistency,chroma,preset
2
+ 04d651c3-3b08-4de9-befe-af12b3b3555b.mp4,11.63755702972412,2.962747573852539,0.2545850100914842,93.50666666666666,12.372051285756838,Glacier,36,1928,952,25.0,0.1429923623800277,0.0728510618209838,0.126477791035855,0.3213473471114912,0.3275197079482676,0.0515626634471215,0.0160338471954067,0.13787563780699,0.092448416088427,0.989902800881153,1,6
3
+ 07484eba-7a60-4754-ba0b-f9c0744af11b.mp4,50.56832885742188,6.884061813354492,0.1361338602421329,88.56666666666666,20.0522675981826,Sea,36,3744,2320,29.97,0.0958104953169822,0.0445181988179683,0.1467628849442237,0.3145919473712218,0.5450120102012216,0.0460680767388741,0.0104968805486957,0.3294798113763631,0.0776948865310934,0.9906222568234484,2,8
4
+ 0cb873d6-4c3a-4ced-84e9-87939b08565d.mp4,19.441584587097168,1.3194904327392578,0.0678694901039583,94.21666666666664,14.191413000600472,Street,36,3928,2176,25.0,0.0367702804505825,0.0250370651483535,0.1093549868154569,0.4941628043122025,0.2712272513396657,0.013138959918234,0.0052625991714497,0.0428705516702607,0.0243741084933209,0.99460809731599,1,7
5
+ 0d0496e7-fe16-443b-9c16-369aa386bb4e.mp4,91.49424362182616,35.15753650665283,0.3842595459007206,89.8,19.181592760557905,Sea,36,3824,2080,29.97,0.4522957503795624,0.1822116822004318,0.1255781912376141,0.3903664236722475,0.3277484580793212,0.0777180727657976,0.0146979351217548,0.1534577499731789,0.1063530938204055,0.9972791267899178,2,6
6
+ 0e4ce3ab-c7c1-4dd6-9d9d-983eabf8acf3.mp4,14.362677574157717,2.4586334228515625,0.1711821079431108,86.85000000000001,22.136717544720863,Glacier,36,2176,1312,29.97,0.0441711656749248,0.0190866943448781,0.2714131776661668,0.5323091666051284,0.1473271014321708,0.0106787879976685,0.0052575784890602,0.5868236919386657,0.0213027581935975,0.9938409873174072,1,8
7
+ 10e3a5e7-dda1-496d-84bb-f826e6567d82.mp4,12.642633438110352,2.115780830383301,0.1673528573568718,86.82666666666667,21.79987671919208,Sea,36,2024,1160,29.97,0.0658735483884811,0.027121203020215,0.2075980188991426,0.5772056769075355,0.1538377535831014,0.0165804881650084,0.0074098287150263,0.2507814274680841,0.0274054024124301,0.99776000034742,2,8
8
+ 12cb34d6-c1d8-4b85-8f0e-60afaf26ffc9.mp4,97.8766794204712,32.654263496398926,0.3336265971602751,99.62,11.553677315658312,Sea,36,3992,2128,29.97,0.1492599844932556,0.0777132287621498,0.1066155836718416,0.4059681511346936,0.3941002960882696,0.0566694557661689,0.016210103717943,0.1896969848217739,0.0992825641885274,0.9999994889713084,2,6
9
+ 12dd2d2b-24f3-4c31-b873-fa4e53803778.mp4,13.259980201721191,1.2535648345947266,0.0945374589949997,92.54,16.066609960586902,Buildings,36,3720,2000,24.0,0.0126855373382568,0.0054733753204345,0.1402558005829288,0.3471829562864572,0.7360188973223698,0.001899417562724,0.0039290150161832,0.0334341397849462,0.0028954301075268,0.9861516260805396,1,7
10
+ 221cab7a-eb1a-4b14-9568-9ac4a4ec9330.mp4,30.4854040145874,8.698417663574219,0.2853305686685992,93.10666666666668,12.963095414146787,Sea,36,1672,1096,29.97,0.9161150455474854,0.379181295633316,0.0543770877669747,0.5162277035227647,0.293133004869604,0.1587936850254368,0.0282400405655304,0.3147559197429539,0.2448316845946984,0.993860990900394,2,6
11
+ 249c9213-36ad-4280-a900-306b96685c0e.mp4,56.18430137634277,6.00376033782959,0.1068583250259574,85.86,26.456385705685307,Sea,36,4352,2512,25.0,0.0763325467705726,0.0321722514927387,0.1179865907502876,0.4856625565126684,0.1818267550762109,0.0330283511692893,0.0083340853452682,0.2281288784423005,0.0552171726448342,0.9998370150268332,2,8
12
+ 2884fdc7-6b49-4008-ae33-54956784cf22.mp4,115.50566577911376,39.40678596496582,0.3411675583111658,99.77666666666666,10.641904721237973,Sea,36,4136,2528,29.97,0.1351182460784912,0.0738693550229072,0.0741262606518029,0.4363434005579151,0.3657297264831183,0.0492047099564184,0.013960901958247,0.0938197538949962,0.0921825936359963,0.9924948222041552,2,6
13
+ 2efd6ba4-b102-4009-b197-da0cdaa634dc.mp4,24.736478805541992,2.1472091674804688,0.0868033475726305,96.65666666666668,14.345898901049,Street,36,4176,2264,25.0,0.0174604374915361,0.0072254240512847,0.1744482783278272,0.3013473596598065,0.2914109880184468,0.0007968369932623,0.0041565479865918,0.7164728393557441,0.0004970667824215,0.945495845282456,1,7
14
+ 31f7bc2c-838f-43c2-9cf8-745cc9ce27ab.mp4,22.826221466064453,1.773118019104004,0.0776789983283077,91.56666666666666,17.673464012040057,Glacier,36,4072,1984,25.0,0.035806518048048,0.0185190923511981,0.1413763511584968,0.45802809033771,0.3093696310212299,0.0120091791965059,0.0057176739598313,0.4397072453894416,0.0214490976931364,0.9821693564696936,2,7
15
+ 329f1c4c-f5df-453d-9cfc-6f2cbef5142b.mp4,9.611075401306152,1.6033363342285156,0.1668217413017716,91.58,14.919262283403194,Buildings,36,3840,2160,29.97002997002997,0.0132886199280619,0.009028784930706,0.1450372010687532,0.3001861801079742,0.5296609669395022,0.011812829539609,0.0035800460415581,0.2275868055555555,0.0218736135223765,0.9926868678702192,1,7
16
+ 34e7d8b9-5fd5-44d6-ba9c-194d4e2078ae.mp4,8.409784317016602,1.186915397644043,0.1411350580350085,93.26666666666668,17.535968238489765,Street,36,1864,1112,25.0,0.0701295137405395,0.046890240162611,0.1179904369005827,0.4931510383715244,0.3031222125304049,0.0228256772264591,0.0097284382209181,0.2783456711026029,0.0409182310803717,0.9759738649373356,2,7
17
+ 39c98272-d26f-4139-afbf-d870b7553fab.mp4,11.697440147399902,2.2339115142822266,0.1909743915021252,85.91333333333334,21.82086911294481,Glacier,36,1912,1152,29.97,0.0379447676241397,0.0166470278054475,0.2989692671433983,0.568221517441918,0.165857213229027,0.0145771588795908,0.0056536531386276,0.511477219897722,0.0302196380317294,0.9934711756986034,1,8
18
+ 40949702-a809-4464-9d14-481e76abd967.mp4,11.222305297851562,4.060033798217773,0.3617825117442707,95.78666666666668,19.67346395047992,Street,36,1992,1080,25.0,0.1722472161054611,0.0929764881730079,0.1250032341526129,0.3511127182416424,0.3858102265027187,0.0427754226783677,0.0143549318114916,0.1648349571124002,0.0433976647330061,0.999515551312002,1,6
19
+ 424df265-1080-4edf-8cfc-b05f62acfffc.mp4,93.02153778076172,38.198049545288086,0.4106366166007205,90.03666666666668,18.992862809167303,Sea,36,3744,2176,29.97,0.5003300309181213,0.2049688547849655,0.0901389832452703,0.4063670264387649,0.2718818422945892,0.0835086546971887,0.0158878316481908,0.2587865257614798,0.1137096314888134,0.999358035236232,2,6
20
+ 59bb4aa2-724b-490a-944c-eaae28496192.mp4,82.74820232391357,38.806517601013184,0.4689711257908306,95.3,17.10664600652759,Sea,36,3632,2096,24.0,0.3662587702274322,0.166818082332611,0.2566843861128125,0.2162887687259836,0.4824837299832892,0.0542928510077232,0.0178386699408292,0.5918705022362714,0.0900308065289033,0.9984225156964288,2,6
21
+ 5e800829-24b2-4ca7-bac4-89a02199fbec.mp4,9.155067443847656,1.3330106735229492,0.1456035885807431,90.11333333333334,18.971216361292488,Buildings,36,1928,1144,29.97,0.0794639736413955,0.0400956831872463,0.1179491268665797,0.5208322161695057,0.2232629198900251,0.0302041924189227,0.0122872879728674,0.263849394036116,0.0527989256594028,0.9977240688080936,1,7
22
+ 6084559b-4f79-480c-ae41-bdb3d0a6987c.mp4,8.81439208984375,0.454263687133789,0.0515365872658657,94.17,28.143217609294314,Buildings,36,3864,2176,24.0,0.0228502452373504,0.010489453561604,0.2066807213167505,0.2309552700244988,0.2557928800116594,0.0037469981199001,0.0021716706299533,0.6112707197915398,0.0058192349630069,0.9988106814283684,1,8
23
+ 61a7d453-d120-440f-abfe-fa05714179b4.mp4,20.49268341064453,2.590888023376465,0.1264299053207778,92.07,16.241323427262568,Buildings,36,3456,2104,24.0,0.0289852488785982,0.0140899522230029,0.2357989120564686,0.193935172888864,0.6341521050899118,0.0047159034232267,0.0054534943774342,0.6904527457987139,0.0081701328378045,0.9847355475512976,1,7
24
+ 64e41231-4092-46c3-906d-5170de857162.mp4,28.37565803527832,4.181161880493164,0.1473503055081539,88.35000000000001,18.958802985591056,Mountain,36,2280,1264,23.976,0.3514604568481445,0.1335129588842392,0.1668962952215819,0.2662084490876815,0.4625996798249243,0.1331960406025612,0.0211052832504113,0.575414307128581,0.2066374153342216,0.9915836759698328,1,6
25
+ 691de276-7b47-4015-bc54-da4343889084.mp4,21.35616779327393,2.661280632019043,0.1246141469658806,92.38666666666668,19.55607154395117,Glacier,36,4208,2040,25.0,0.0283349398523569,0.0150849567726254,0.147305984712665,0.4657045179866474,0.478251971736034,0.0079452614379084,0.0047814832068979,0.5164016097567037,0.0134434643629314,0.9986614345096868,2,7
26
+ 733704b4-12f7-4977-b920-6896bc9ca77c.mp4,42.62840366363525,4.617931365966797,0.1083299154808882,84.61,26.44615321681865,Sea,36,3632,2112,25.0,0.0834942460060119,0.0357695259153842,0.1235101446790105,0.5205489246068594,0.2374145989757425,0.0421646166371868,0.0080157189319531,0.1772868761958795,0.0679565478574289,0.9985238316412702,2,8
27
+ 81e87893-da5e-4db2-afd1-b1bb11a03de7.mp4,11.973058700561523,2.250288963317871,0.1879460394871643,94.15666666666664,12.184221110468258,Sea,36,1848,1080,29.97,0.158597245812416,0.0801888853311538,0.0707714670215206,0.4440622167310621,0.4276124882238716,0.0134389530222863,0.0096513582393527,0.2976868553257442,0.0195476591309924,0.9545209309874344,2,6
28
+ 8b6d7852-7f21-47df-aa03-fc8c0f29e6d3.mp4,36.639710426330566,6.357483863830566,0.1735134855012899,98.35666666666668,11.7227002191702,Buildings,36,4216,2472,25.0,0.038616944104433,0.0222245398908853,0.1535103188816618,0.5920864945591565,0.2681580551402431,0.0082668454687439,0.0051134812335173,0.1357015141373388,0.0153606061513236,0.9985361279804812,1,7
29
+ 8f0464f5-2e81-4d50-b4bb-0667fadd55b6.mp4,34.017616271972656,16.64235496520996,0.4892275470495476,97.63333333333333,14.750361283262205,Street,36,1944,1000,30.0,0.7986264824867249,0.3549840152263641,0.1570230198475594,0.5378053457597031,0.2279299167540815,0.1251349451303155,0.0310485574106375,0.3349941700960219,0.180920524691358,0.986462531267651,1,6
30
+ 93495912-fd6c-435a-9fcb-c9cc978c122e.mp4,14.999189376831056,2.030162811279297,0.1353515020228525,90.58,15.28308285240923,Sea,36,2008,1248,29.545,0.1255028247833252,0.0577357523143291,0.1450512624336307,0.5325459562996547,0.2646494876660611,0.0453523778816358,0.0071958565774063,0.0897749812714952,0.0797762475738073,0.993942635378546,2,6
31
+ 9a049c4c-bb40-4692-8b95-cb2ca94d339f.mp4,7.705256462097168,0.7953510284423828,0.1032218761769688,89.89333333333333,32.48977789954205,Sea,36,2072,1168,29.97,0.0590476691722869,0.0255420710891485,0.0779400273458398,0.4569901856213298,0.4663660800924096,0.0096129795402055,0.0046418259541193,0.0497310850478658,0.0130963813005765,0.9963151955987876,2,8
32
+ 9d860f37-78b5-4e91-bbff-91bbfaaaa32a.mp4,24.54138946533203,3.542244911193848,0.1443375859462944,86.88999999999999,18.815549968769943,Mountain,36,2176,1128,23.976,0.3589254915714264,0.1361661404371261,0.1674924357608167,0.2623429547776905,0.4926483928925658,0.1339137300531915,0.0214552866915861,0.4039163537755528,0.2070070090868794,0.991671204099078,1,6
33
+ a3fa72da-8a4e-466c-9a11-fc2a67863b9e.mp4,66.18983554840088,15.258377075195312,0.2305244747743438,98.00666666666666,11.54028731617105,Sea,36,4064,2328,29.97,0.0872727632522583,0.0455792434513568,0.0999884181943172,0.4212085293801522,0.3973935582882845,0.0268627222177124,0.0114057408645749,0.0736418196597848,0.0521458532043997,0.9822594713112536,2,6
34
+ a508a47e-295b-4be0-ab3f-4b6478dacc65.mp4,150.44969272613525,62.874778747558594,0.4179123108081719,92.61,15.8732601360474,Sea,36,4360,2352,59.94,0.3012926876544952,0.1273589581251144,0.1922401876107427,0.5702624508009116,0.1724064290599012,0.085398268634671,0.0167182801912228,0.3027350657388337,0.1516859553454409,0.9744751753912574,2,6
35
+ a5f32e7e-a5f3-4a7e-b7fd-dc0fb551e243.mp4,27.75857830047608,8.89590835571289,0.3204742065468217,93.04333333333334,18.556904219657586,Sea,36,1896,1120,29.97,0.4847494661808014,0.2182989865541458,0.1002119651271572,0.4154083440689919,0.3129233998451701,0.1315088469459513,0.0165209782620271,0.2850421313039984,0.1916254614978902,0.9980037344579182,2,6
36
+ a75909eb-460b-4eb8-ab69-1f81413fde39.mp4,15.16309642791748,1.656632423400879,0.1092542299177611,91.09333333333332,17.147321929463487,Glacier,36,3504,2216,24.0,0.0099504310637712,0.0039873127825558,0.1837182655713437,0.320486532314655,0.7251103568436426,0.0009946943908697,0.0028258475164572,0.0432883306133887,0.0012447738943013,0.974405000500192,1,8
37
+ a8fb16e1-2992-4ab7-95b7-47d48938f465.mp4,56.77720260620117,6.564875602722168,0.1156252034510265,87.28666666666668,25.996976327787028,Sea,36,4416,2512,25.0,0.0753649845719337,0.0320221297442913,0.1090954151336163,0.5430451944241572,0.1567155809969492,0.0323188429836302,0.0074781634223957,0.2131279520139081,0.0550013467962475,0.9987616523664528,2,8
38
+ af18139c-acaf-4c43-977a-9a86fb3f3602.mp4,15.229940414428713,1.547590255737305,0.1016149908420608,94.05666666666666,14.702759137373825,Glacier,36,3920,2208,24.0,0.0150572964921593,0.006208827253431,0.1435621928273399,0.3124642683160221,0.8005967936691585,0.0014279398353544,0.003335137773926,0.0207769520851819,0.002143411712511,0.988551496232551,1,7
39
+ b2b4e988-b71d-4d6f-a5dd-8db222348faa.mp4,8.236392974853516,1.106584548950195,0.1343530538584914,91.45666666666666,18.26996100063993,Mountain,36,1992,1232,25.0,0.0576150827109813,0.0262715872377157,0.0962681090485671,0.5841186732542597,0.1946375993959711,0.0105294011544011,0.0063949679024517,0.1362384060049722,0.0184834304751473,0.989757914392482,1,7
40
+ b872dec9-a527-443e-807b-8a650fb44aa5.mp4,31.921814918518063,4.167214393615723,0.1305444068344088,90.35,20.685591731501987,Glacier,36,3672,2200,29.97,0.0734859108924865,0.0299688708037138,0.122406591632788,0.3481631003097746,0.4225727329685422,0.0204849970291146,0.0085126602401336,0.083881626724764,0.0360843731431966,0.9853751111654804,1,8
41
+ bb85c24e-dfb2-4e74-a538-7b88444ba7a2.mp4,7.403351783752441,1.078202247619629,0.1456370410475259,86.96666666666668,31.55410760665986,Sea,36,2064,1136,29.97,0.0316026173532009,0.0123316012322902,0.3639183350001875,0.4339056100931475,0.6477346277180488,0.0062771249590566,0.0054633522716661,0.8203375209265932,0.0106365238426684,0.999820633741268,2,8
42
+ bd22f174-e591-4d7a-a5fa-40447e18191e.mp4,14.803239822387695,2.936840057373047,0.1983917096939491,88.06666666666666,19.394910594585504,Mountain,36,1840,1008,59.94,0.172185018658638,0.0587177462875843,0.0989175467097531,0.413866736610103,0.3300058145238772,0.0461997857717966,0.0112428820381561,0.0506351363008971,0.0771057086783988,0.9926592185787358,2,6
43
+ c6b21693-9149-46a7-8062-b4e36d861374.mp4,6.766033172607422,0.8436651229858398,0.1246912483966904,91.64333333333332,34.748478048384115,Mountain,36,1800,1040,25.0,0.1755229383707046,0.0655823424458503,0.1886238112680818,0.3749341251885369,0.472089366515837,0.0616330128205128,0.01280546374619,0.3262396723646723,0.0449030448717948,0.9997276437070556,2,7
44
+ d31d550a-bebb-4124-bd43-8b60d0e18de8.mp4,17.65333652496338,1.5875320434570312,0.0899281584085886,92.79,18.366078180220185,Street,36,2256,1208,23.976,0.321790337562561,0.1782573610544204,0.1331180156782398,0.3457849007320899,0.3365648953207698,0.0981379798584691,0.0206291632105906,0.4263416271350961,0.1503226495350147,0.9993916711217388,1,6
45
+ d863f7e4-dceb-4f47-8a78-dd6d1e4e880d.mp4,29.070735931396484,6.318779945373535,0.2173587885867462,85.97333333333334,20.767000095983462,Sea,36,2200,1240,59.94,0.1871851682662964,0.0643177106976509,0.1103539881428786,0.3952507633258582,0.3379617215801276,0.0673505620723362,0.0123686343431472,0.2884670087976539,0.10835392228739,0.9977341311597951,2,6
46
+ dd477309-fa8a-4317-94ff-d8b54bc3cad1.mp4,21.595118522644043,2.3091840744018555,0.1069308358729547,93.02666666666666,12.38130718901219,Buildings,36,3944,2168,25.0,0.0399475842714309,0.0225663352757692,0.0982543567243773,0.4887902318051936,0.4495643316600509,0.0099232115546307,0.0066649932414293,0.1681051635567065,0.0187829684775042,0.964893394653431,2,7
47
+ e0ece203-f76b-40b9-8285-cb9fd999c5c4.mp4,48.40852928161621,4.847820281982422,0.1001439282276118,95.54333333333334,15.267127113506389,Buildings,36,3912,2192,24.0,0.1240128055214881,0.0678207352757453,0.1008983220628024,0.3571532536652205,0.3761971421338625,0.0539492387886296,0.012217029929161,0.2409559114385085,0.0994769276267669,0.9933512871772088,1,6
48
+ e199cb9b-752f-4aa1-a4b9-1885fa53a026.mp4,67.37089347839355,9.13538932800293,0.1355984588646241,93.14,18.97621105248905,Buildings,36,3704,2192,24.0,0.3928792476654053,0.1705749481916427,0.1482475339820311,0.3366086798822518,0.1580182770650997,0.0791778993446947,0.0160163554052511,0.3772218204295481,0.1360922079700777,0.9976828925927232,1,6
49
+ e7f5193f-e47b-4376-977d-feb98314b110.mp4,18.898674964904785,2.464480400085449,0.1304049307510732,98.17,13.702267426394744,Street,36,1976,1112,29.97,0.2872275412082672,0.1347596794366836,0.1224473220403082,0.450834061852685,0.4047301679970621,0.0687426273556053,0.0160518120974302,0.2834111253507316,0.1179757358081146,0.989530204248128,2,6
50
+ f11482e6-3485-46a8-9ade-92b46a803067.mp4,17.065518379211426,3.2750844955444336,0.1919123945003639,88.09666666666665,23.87321348719044,Sea,36,2016,1184,30.0,0.131056860089302,0.0564496926963329,0.2015653390023331,0.5128782488502158,0.2638884277747635,0.0529823908730158,0.0086417753870288,0.5257810824217074,0.0851019127386314,0.999383320097934,2,7
51
+ f4f67a32-a8f5-43ac-93fb-6a41a45696de.mp4,48.70376110076904,11.58397102355957,0.2378455125794517,94.2,27.01278655262587,Street,36,3920,2072,24.0,0.5899763107299805,0.3269930779933929,0.1248712283772103,0.4635716519555055,0.3184764832573956,0.1427197012975074,0.030318242808183,0.2410834121293304,0.107225469821133,0.9999854710223858,2,6
52
+ fcd1e637-3ac0-49af-920e-6bc5ae2e32cf.mp4,46.41104698181152,11.405869483947754,0.245757642322047,90.84,23.09361398873996,Sea,36,3696,2256,25.0,0.1243470683693885,0.0575859062373638,0.4032672850400358,0.0772454033864781,0.3632161264328427,0.0128380995236048,0.0054249467017749,0.6764255555811407,0.0217236959258235,0.9999686643274333,2,8
53
+ fd4e4b88-600d-4a77-acdc-a1315ce301cc.mp4,20.68675708770752,1.9913969039916992,0.09626433449905136,92.33999999999999,14.531413968873949,Glacier,36,4568,2672,24.0,0.0160443242639303,0.0065641249530017,0.1358391283293425,0.3137757971488773,0.7292011663634032,0.0011647567387117,0.0032998408811787,0.1209636331540072,0.0016134270425873,0.9884601788362084,1,7
iqf_scores_1.csv ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ file_name,ori_luma_qp,ori_chroma,ori_preset,brightness,contrast,colorfulness,sharpness,quality_factor,mos_zscore,luma_qp,chroma,preset,final_scores
2
+ 04d651c3-3b08-4de9-befe-af12b3b3555b.mp4,32,0,4,1.8469307366129406e-16,0.9999998410542806,1.0,0.9999999602635702,0.298225900199812,0.0,35,5,10,90.31333333333333
3
+ 07484eba-7a60-4754-ba0b-f9c0744af11b.mp4,31,-1,4,0.6979172627131144,0.6313334306081136,0.6084306836128235,0.6348755955696106,0.6509576241175333,0.7038862506548563,38,0,10,84.82666666666667
4
+ 0cb873d6-4c3a-4ced-84e9-87939b08565d.mp4,35,1,4,2.1057123452742498e-07,0.9972970485687256,0.9999999602635702,0.9853918751080832,0.3318715903168057,6.486347245594561e-25,36,6,10,92.60666666666667
5
+ 0d0496e7-fe16-443b-9c16-369aa386bb4e.mp4,31,-1,4,0.5955032904942831,0.7397701740264893,0.5834726889928182,0.7210836410522461,0.65736456712087,0.4505663216114044,37,0,10,86.69333333333333
6
+ 0e4ce3ab-c7c1-4dd6-9d9d-983eabf8acf3.mp4,31,-1,4,0.6425169706344604,0.6254289746284485,0.5407780408859253,0.6723130941390991,0.7194178899129232,0.6874635815620422,37,-1,10,81.39333333333333
7
+ 10e3a5e7-dda1-496d-84bb-f826e6567d82.mp4,31,-1,4,0.666383703549703,0.644948422908783,0.5396574338277181,0.6548968553543091,0.6909757057825724,0.6799997289975485,37,-1,10,80.31333333333335
8
+ 12cb34d6-c1d8-4b85-8f0e-60afaf26ffc9.mp4,31,-1,4,0.559302568435669,0.6554734110832214,0.517133355140686,0.6597744027773539,0.6716094811757406,0.6479589343070984,37,-1,10,99.25333333333333
9
+ 12dd2d2b-24f3-4c31-b873-fa4e53803778.mp4,33,2,4,0.0304905549855902,0.8879064122835795,0.9823326071103414,0.992975890636444,0.0004040396706827,2.798745185113234e-08,35,7,10,90.84666666666668
10
+ 221cab7a-eb1a-4b14-9568-9ac4a4ec9330.mp4,31,-1,4,0.6206316550572714,0.6937900980313619,0.5681640307108561,0.6865283250808716,0.6309727231661478,0.5269437432289124,37,0,10,88.66666666666667
11
+ 249c9213-36ad-4280-a900-306b96685c0e.mp4,31,-1,4,0.6744417746861776,0.6287276744842529,0.5382514794667562,0.6795710523923238,0.7059144775072733,0.6740560928980509,37,-1,10,80.68666666666667
12
+ 2884fdc7-6b49-4008-ae33-54956784cf22.mp4,31,-1,4,0.5739724636077881,0.6645207007726034,0.5748539765675863,0.6702662507692972,0.6798134446144104,0.6453063488006592,37,0,10,99.71333333333332
13
+ 2efd6ba4-b102-4009-b197-da0cdaa634dc.mp4,33,0,4,0.2123574561207135,0.923816998799642,0.5404039584100246,0.557942457186679,0.5570492148494531,0.1948199073473612,42,0,10,94.18333333333334
14
+ 31f7bc2c-838f-43c2-9cf8-745cc9ce27ab.mp4,32,0,4,0.2436689063906669,0.865463117758433,0.5987633650268739,0.663530599674465,0.0083957752296021,0.0033863165364441,39,1,11,87.52999999999999
15
+ 329f1c4c-f5df-453d-9cfc-6f2cbef5142b.mp4,35,1,4,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,41,2,10,84.48
16
+ 34e7d8b9-5fd5-44d6-ba9c-194d4e2078ae.mp4,33,0,4,0.6085565884908041,0.7023536364237467,0.5811308026313782,0.6709411342938741,0.6804569562276205,0.6665542125701904,39,1,10,88.88666666666666
17
+ 39c98272-d26f-4139-afbf-d870b7553fab.mp4,31,-1,4,0.6213184396425883,0.6266296108563741,0.5339837074279785,0.6631546020507812,0.7125220696131388,0.6837653716405233,37,-1,10,78.45333333333333
18
+ 40949702-a809-4464-9d14-481e76abd967.mp4,33,0,4,3.0931066325599755e-11,0.6665612919865964,1.0,0.5694492856661543,0.013828640182813,4.3167463616559045e-17,43,5,11,88.24666666666667
19
+ 424df265-1080-4edf-8cfc-b05f62acfffc.mp4,31,-1,4,0.572777271270752,0.8049134612083435,0.5622189939022064,0.7238964438438416,0.6895942687988281,0.4115677277247111,36,0,10,87.91000000000001
20
+ 4df1a23d-73f2-4cab-b43c-5538b566a458.mp4,35,1,4,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,41,2,10,84.48
21
+ 59bb4aa2-724b-490a-944c-eaae28496192.mp4,31,-1,4,9.296274529428212e-07,0.3862022735666339,0.0247434326447546,1.0,1.0,0.0171932382619767,35,-6,10,94.98666666666668
22
+ 5e800829-24b2-4ca7-bac4-89a02199fbec.mp4,35,1,4,0.6375203331311544,0.6609386603037516,0.5363780458768209,0.6825437943140665,0.7010063727696737,0.6790463924407959,41,1,10,82.86
23
+ 5fe0ff99-37a3-4760-81a4-f441876cb867.mp4,35,1,4,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,41,2,10,84.48
24
+ 6084559b-4f79-480c-ae41-bdb3d0a6987c.mp4,33,2,4,0.5323069393634796,0.7520566980044047,0.5722926259040833,0.693462073802948,0.5514254768689474,0.2662951275706291,39,3,10,91.99666666666667
25
+ 61a7d453-d120-440f-abfe-fa05714179b4.mp4,35,1,4,0.5853416124979655,0.6666666666666666,1.0,0.3333347989194711,2.981454431780764e-25,0.0,50,6,11,79.58666666666667
26
+ 64e41231-4092-46c3-906d-5170de857162.mp4,30,0,4,0.745102326075236,0.670194149017334,0.5261250138282776,0.7535956104596456,0.707586407661438,0.3179372052351634,35,0,10,84.63666666666666
27
+ 691de276-7b47-4015-bc54-da4343889084.mp4,32,0,4,0.6291013558705648,0.647834857304891,0.6641747156778971,0.6670735081036886,0.7004674077033997,0.6992351412773132,38,2,10,88.96666666666665
28
+ 733704b4-12f7-4977-b920-6896bc9ca77c.mp4,31,-1,4,0.6398445963859558,0.6260692278544108,0.5751756628354391,0.6669502059618632,0.7081162532170614,0.6691677371660868,37,0,10,79.04333333333334
29
+ 81e87893-da5e-4db2-afd1-b1bb11a03de7.mp4,33,2,4,7.679543605343628e-09,1.0,1.0,0.3358941231754215,4.8890841564280026e-21,4.577665144872662e-36,47,7,11,78.89666666666666
30
+ 890c5911-8ee1-4faa-a801-c605f0dde8e0.mp4,35,1,4,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,41,2,10,84.48
31
+ 8b6d7852-7f21-47df-aa03-fc8c0f29e6d3.mp4,35,1,4,0.6386719544728597,0.6828855474789938,0.5790578722953796,0.6756150126457214,0.7062546610832214,0.6745621760686239,41,2,10,96.22000000000001
32
+ 8f0464f5-2e81-4d50-b4bb-0667fadd55b6.mp4,33,0,4,0.6737418174743652,0.6781927148501078,0.5551733573277792,0.6866800983746847,0.6682468851407369,0.6677144169807434,39,1,10,93.66000000000001
33
+ 93495912-fd6c-435a-9fcb-c9cc978c122e.mp4,31,-1,4,0.3303557277324671,0.572416496075069,0.8744631012280782,0.5750492256289969,0.245182732741038,0.1620057622591654,41,3,10,82.59333333333333
34
+ 9a049c4c-bb40-4692-8b95-cb2ca94d339f.mp4,31,-1,4,0.5415121912956238,0.6919364531834921,0.5953385432561239,0.7154134511947632,0.6035712758700053,0.5871815880139669,36,0,10,86.02666666666669
35
+ 9d860f37-78b5-4e91-bbff-91bbfaaaa32a.mp4,30,0,4,0.5807816584904989,0.6518141428629557,0.5270594159762064,0.6739723483721415,0.6623684565226237,0.5438634157180786,36,0,10,82.79
36
+ a3fa72da-8a4e-466c-9a11-fc2a67863b9e.mp4,31,-1,4,0.2128963904734471,0.8745509584744772,0.8768628239631653,0.579653590063875,0.24392955436218,0.2292507886886597,39,3,10,95.11666666666667
37
+ a508a47e-295b-4be0-ab3f-4b6478dacc65.mp4,31,-1,4,0.626129150390625,0.6703311999638876,0.562980075677236,0.6703017155329386,0.6834884285926819,0.6630960901578268,37,0,10,88.67
38
+ a5f32e7e-a5f3-4a7e-b7fd-dc0fb551e243.mp4,31,-1,4,0.6408935189247131,0.7019673387209574,0.5994003017743429,0.701171875,0.6946845253308614,0.6745195984840393,36,0,10,90.31333333333333
39
+ a75909eb-460b-4eb8-ab69-1f81413fde39.mp4,35,1,4,0.0452174662328464,0.6664746296834588,0.9997160037358602,0.662308836724454,0.2454252270120681,4.135975021220975e-11,43,6,10,85.72666666666667
40
+ a8fb16e1-2992-4ab7-95b7-47d48938f465.mp4,31,-1,4,0.6491552790006002,0.6554122765858968,0.5638013680775961,0.663050651550293,0.7081366777420044,0.6996987660725912,37,0,10,82.66666666666667
41
+ acfbb4ee-70e5-45bf-8aa8-349e388499ed.mp4,35,1,4,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,41,2,10,84.48
42
+ af18139c-acaf-4c43-977a-9a86fb3f3602.mp4,33,2,4,0.2569143105560701,0.3665943775946895,0.7403472264607748,0.6492060224215189,0.3337878490177342,0.0003729414893314,41,4,10,90.76999999999998
43
+ b2b4e988-b71d-4d6f-a5dd-8db222348faa.mp4,33,2,4,0.6797566215197245,0.6921680370966593,0.5756566723187765,0.6673476894696554,0.7057122588157654,0.6777393817901611,39,3,10,86.19
44
+ b872dec9-a527-443e-807b-8a650fb44aa5.mp4,33,2,4,0.5625234958715737,0.8364881674448649,0.8529369036356608,0.3062493354858209,0.2298784634719292,0.2218764863639043,47,6,10,76.47
45
+ bb85c24e-dfb2-4e74-a538-7b88444ba7a2.mp4,31,-1,4,0.5719117522239685,0.5746606588363647,0.7491706609725952,0.6051486531893412,0.6949742039044698,0.6399241884549459,38,1,10,79.57333333333332
46
+ bd22f174-e591-4d7a-a5fa-40447e18191e.mp4,31,-1,4,0.3112848651896153,0.9546863834063212,0.6849623595674833,0.8972047170003256,0.5202026833430864,0.2215489234929057,35,1,10,84.64999999999999
47
+ bdc6f64e-fe6c-4f98-b37d-a8405f0674a6.mp4,35,1,4,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,41,2,10,84.48
48
+ c6b21693-9149-46a7-8062-b4e36d861374.mp4,30,0,4,0.6675481796264648,0.6689058740933737,0.6905355652173361,0.6873549222946167,0.6984812021255493,0.6681686639785767,36,2,10,89.67333333333333
49
+ ce34e0c1-7378-4fc8-96c7-44ddcbbf1487.mp4,31,-1,4,0.626129150390625,0.6703311999638876,0.562980075677236,0.6703017155329386,0.6834884285926819,0.6630960901578268,37,0,10,88.67
50
+ d31d550a-bebb-4124-bd43-8b60d0e18de8.mp4,33,0,4,0.5594322880109152,0.7071808179219564,0.5779878795146942,0.7282688617706299,0.6871213515599569,0.5582586626211802,38,1,10,89.88333333333333
51
+ d863f7e4-dceb-4f47-8a78-dd6d1e4e880d.mp4,33,2,4,0.7645663022994995,0.7623209357261658,0.3754141006926754,0.7709534366925558,0.8046964406967163,0.4424456845930156,37,1,10,80.22333333333334
52
+ dd477309-fa8a-4317-94ff-d8b54bc3cad1.mp4,35,1,4,0.5718956192334493,0.6503244837125143,0.5420317649841309,0.6384625236193339,0.6779917677243551,0.6346049110094706,42,1,10,87.84666666666665
53
+ e0ece203-f76b-40b9-8285-cb9fd999c5c4.mp4,35,1,4,0.7104902664820353,0.6507590810457865,0.6978453199068705,0.6581440766652426,0.7403779625892639,0.2868792239266137,42,3,10,88.86666666666666
54
+ e199cb9b-752f-4aa1-a4b9-1885fa53a026.mp4,35,1,4,0.2177942705724667,0.7863604227701823,0.6638821821833668,0.7953539888064066,0.3359929546713829,0.0008118189095209,40,3,10,86.86666666666666
55
+ e4bb727c-b404-4141-bfbe-af71df54c3ff.mp4,35,1,4,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,41,2,10,84.48
56
+ e7f5193f-e47b-4376-977d-feb98314b110.mp4,33,0,4,0.6336689988772074,0.6886950929959615,0.5957454244295756,0.7071948448816935,0.67882106701533,0.6200438141822815,38,1,10,93.98
57
+ f11482e6-3485-46a8-9ade-92b46a803067.mp4,31,-1,4,0.5987781286239624,0.6132558385531107,0.6389955282211304,0.6446781953175863,0.7036546866099039,0.6583541433016459,38,0,10,83.53666666666668
58
+ f4f67a32-a8f5-43ac-93fb-6a41a45696de.mp4,33,0,4,0.6998077034950256,0.6582560737927755,0.5905927220980326,0.6627445220947266,0.6663028597831726,0.6565006971359253,39,1,10,90.92
59
+ fcd1e637-3ac0-49af-920e-6bc5ae2e32cf.mp4,31,-1,4,0.0272201643213868,0.3309482576635976,0.4792167643706004,0.998989482720693,0.6663707869944121,0.0050284543152277,35,-1,10,89.79333333333334
60
+ fd4e4b88-600d-4a77-acdc-a1315ce301cc.mp4,33,2,4,0.4362088193496068,0.4098552847281098,0.7522272070248922,0.8208715716997782,0.3990619123778742,0.4480555057525927,37,5,10,91.00666666666666
61
+ test1.mp4,32,0,4,0.5417211453119913,0.6767938733100891,0.557605524857839,0.6215428908665975,0.6766568620999655,0.5881443421045939,39,1,10,85.44333333333333
62
+ test10.mp4,32,0,4,0.3333333706116344,0.6667284226520375,1.0,1.0,2.8805363963881653e-13,0.0,35,5,10,82.65666666666665
63
+ test11.mp4,31,-1,4,0.4099834094134469,0.4834768225749333,0.3692534565925598,0.7831077774365743,0.783106525739034,0.629439910252889,35,-2,10,92.98
64
+ test12_gray.mp4,33,0,4,0.018794454264411,0.9975093007087708,0.5747074456497406,0.9784387747446696,0.2222395691399773,0.0231209589789311,35,1,10,89.79
65
+ test13_gray.mp4,35,1,4,0.5948363542556763,0.7572359442710876,0.5567836960156759,0.7168492476145426,0.7041877706845602,0.4451386034488678,40,2,10,97.55333333333333
66
+ test14.mp4,32,0,4,2.398985454732383e-10,0.999976634979248,0.3325004479909856,0.9213998317718506,0.0472111580893397,2.4258483184696295e-17,35,-2,10,90.44999999999999
67
+ test15.mp4,30,-2,4,0.6469272573788961,0.646748940149943,0.5141305724779764,0.6336438258488973,0.6843962669372559,0.6492714285850525,37,-2,10,85.60000000000001
68
+ test2.mp4,32,0,4,0.591302216053009,0.660931130250295,0.5407073299090067,0.6467535098393759,0.7081582148869833,0.6751541495323181,39,0,10,84.26
69
+ test3.mp4,33,0,4,0.6335959037144979,0.6653205553690592,0.5678872664769491,0.6906165877978007,0.6796593268712362,0.6661312778790792,39,1,10,91.08999999999999
70
+ test4.mp4,31,-1,4,0.6273186802864075,0.6763830780982971,0.577736496925354,0.6883265972137451,0.6920067270596822,0.6691595713297526,37,0,10,80.92333333333333
71
+ test5.mp4,32,0,4,0.3322414370577034,0.836751659711202,0.6209217806657156,0.9802228212356568,0.283252388325516,0.1214854717254638,35,1,10,87.76666666666667
72
+ test6.mp4,32,0,4,7.503743552443134e-10,0.6666666666703399,0.9999998807907104,0.0003142244143721,0.3394560635861126,1.5046710818080233e-23,50,5,11,79.53
73
+ test7.mp4,31,-1,4,0.5248274604479471,0.5492491523424784,0.7139172554016113,0.6525176564852396,0.7086620926856995,0.6403847138086954,38,1,10,79.80333333333334
74
+ test8.mp4,33,2,4,0.0,0.6666666666666666,0.6666666667085034,0.6666666666666666,1.1066234416402631e-20,0.0,41,4,11,88.39999999999999
75
+ test9.mp4,31,-1,4,0.5562724669774374,0.5891389648119608,0.6328761577606201,0.6466207702954611,0.7093602021535238,0.6497571667035421,38,0,10,76.86
iqf_scores_2.csv ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ file_name,input_size,output_size,final_compression,final_score,ori_luma_qp,ori_chroma,ori_preset,brightness,contrast,colorfulness,sharpness,quality_factor,mos_zscore,luma_qp,chroma,preset
2
+ 04d651c3-3b08-4de9-befe-af12b3b3555b.mp4,11.63755702972412,2.6749343872070312,77.01463992507333,91.75666666666666,28.1,0.0,4,1.8469307366129406e-16,0.9999998410542806,1.0,0.9999999602635702,0.298225900199812,0.0,37,0,6
3
+ 07484eba-7a60-4754-ba0b-f9c0744af11b.mp4,50.56832885742188,7.134676933288574,85.89101697743483,87.09666666666665,26.691666666666663,-1.0,4,0.6979172627131144,0.6313334306081136,0.6084306836128235,0.6348755955696106,0.6509576241175333,0.7038862506548563,34,0,11
4
+ 0cb873d6-4c3a-4ced-84e9-87939b08565d.mp4,19.441584587097168,1.293718338012695,93.34561268801464,92.84,26.13,1.0,4,2.1057123452742498e-07,0.9972970485687256,0.9999999602635702,0.9853918751080832,0.3318715903168057,6.486347245594561e-25,35,0,11
5
+ 0d0496e7-fe16-443b-9c16-369aa386bb4e.mp4,91.49424362182616,33.905229568481445,62.94277298075475,87.64666666666666,28.391666666666666,-1.0,4,0.5955032904942831,0.7397701740264893,0.5834726889928182,0.7210836410522461,0.65736456712087,0.4505663216114044,36,0,10
6
+ 0e4ce3ab-c7c1-4dd6-9d9d-983eabf8acf3.mp4,14.362677574157717,2.823794364929199,80.33935977223385,87.38,25.425,-1.0,4,0.6425169706344604,0.6254289746284485,0.5407780408859253,0.6723130941390991,0.7194178899129232,0.6874635815620422,33,0,8
7
+ 10e3a5e7-dda1-496d-84bb-f826e6567d82.mp4,12.642633438110352,2.3760366439819336,81.2061572803374,87.05,25.875,-1.0,4,0.666383703549703,0.644948422908783,0.5396574338277181,0.6548968553543091,0.6909757057825724,0.6799997289975485,33,0,7
8
+ 12cb34d6-c1d8-4b85-8f0e-60afaf26ffc9.mp4,97.8766794204712,30.80884456634521,68.52279342866483,99.04,31.183333333333337,-1.0,4,0.559302568435669,0.6554734110832214,0.517133355140686,0.6597744027773539,0.6716094811757406,0.6479589343070984,38,0,10
9
+ 12dd2d2b-24f3-4c31-b873-fa4e53803778.mp4,13.259980201721191,1.3995704650878906,89.44515418728739,91.7,24.0,2.0,4,0.0304905549855902,0.8879064122835795,0.9823326071103414,0.992975890636444,0.0004040396706827,2.798745185113234e-08,33,0,10
10
+ 221cab7a-eb1a-4b14-9568-9ac4a4ec9330.mp4,30.4854040145874,8.113468170166016,73.38572857265173,91.52333333333333,28.975,-1.0,4,0.6206316550572714,0.6937900980313619,0.5681640307108561,0.6865283250808716,0.6309727231661478,0.5269437432289124,36,0,6
11
+ 249c9213-36ad-4280-a900-306b96685c0e.mp4,56.18430137634277,6.987162590026856,87.56385250174365,84.53999999999999,25.06,-1.0,4,0.6744417746861776,0.6287276744842529,0.5382514794667562,0.6795710523923238,0.7059144775072733,0.6740560928980509,32,0,12
12
+ 2884fdc7-6b49-4008-ae33-54956784cf22.mp4,115.50566577911376,39.44039535522461,65.85414655706319,99.71333333333332,29.975,-1.0,4,0.5739724636077881,0.6645207007726034,0.5748539765675863,0.6702662507692972,0.6798134446144104,0.6453063488006592,37,0,11
13
+ 2efd6ba4-b102-4009-b197-da0cdaa634dc.mp4,24.736478805541992,3.4170312881469727,86.18626638411682,97.38,26.04,0.0,4,0.2123574561207135,0.923816998799642,0.5404039584100246,0.557942457186679,0.5570492148494531,0.1948199073473612,32,0,11
14
+ 31f7bc2c-838f-43c2-9cf8-745cc9ce27ab.mp4,22.826221466064453,2.654613494873047,88.37033322041654,90.9,23.43,0.0,4,0.2436689063906669,0.865463117758433,0.5987633650268739,0.663530599674465,0.0083957752296021,0.0033863165364441,31,0,11
15
+ 329f1c4c-f5df-453d-9cfc-6f2cbef5142b.mp4,9.611075401306152,1.5621337890625,83.74652446436738,88.86,26.85483870967742,-1.0,4,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,34,0,11
16
+ 34e7d8b9-5fd5-44d6-ba9c-194d4e2078ae.mp4,8.409784317016602,1.250507354736328,85.13032787052558,93.57666666666668,26.86,0.0,4,0.6085565884908041,0.7023536364237467,0.5811308026313782,0.6709411342938741,0.6804569562276205,0.6665542125701904,34,0,6
17
+ 39c98272-d26f-4139-afbf-d870b7553fab.mp4,11.697440147399902,2.55403995513916,78.16582155620715,86.31666666666666,25.95833333333333,-1.0,4,0.6213184396425883,0.6266296108563741,0.5339837074279785,0.6631546020507812,0.7125220696131388,0.6837653716405233,33,0,7
18
+ 40949702-a809-4464-9d14-481e76abd967.mp4,11.222305297851562,3.6306753158569336,67.64768717749995,94.60333333333334,28.79,0.0,4,3.0931066325599755e-11,0.6665612919865964,1.0,0.5694492856661543,0.013828640182813,4.3167463616559045e-17,36,0,6
19
+ 424df265-1080-4edf-8cfc-b05f62acfffc.mp4,93.02153778076172,36.84726619720459,60.38845725809408,87.91000000000001,28.941666666666663,-1.0,4,0.572777271270752,0.8049134612083435,0.5622189939022064,0.7238964438438416,0.6895942687988281,0.4115677277247111,36,0,10
20
+ 4df1a23d-73f2-4cab-b43c-5538b566a458.mp4,9.611075401306152,1.5621337890625,83.74652446436738,88.86,26.85483870967742,-1.0,4,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,34,0,11
21
+ 59bb4aa2-724b-490a-944c-eaae28496192.mp4,82.74820232391357,51.761945724487305,37.44644080379192,96.74,29.125,-1.0,4,9.296274529428212e-07,0.3862022735666339,0.0247434326447546,1.0,1.0,0.0171932382619767,33,0,10
22
+ 5e800829-24b2-4ca7-bac4-89a02199fbec.mp4,9.155067443847656,1.032573699951172,88.72128789564432,89.29,28.866666666666667,1.0,4,0.6375203331311544,0.6609386603037516,0.5363780458768209,0.6825437943140665,0.7010063727696737,0.6790463924407959,36,0,6
23
+ 5fe0ff99-37a3-4760-81a4-f441876cb867.mp4,9.611075401306152,1.5621337890625,83.74652446436738,88.86,26.85483870967742,-1.0,4,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,34,0,11
24
+ 6084559b-4f79-480c-ae41-bdb3d0a6987c.mp4,8.81439208984375,0.6471805572509766,92.65768358550012,94.24,20.84375,2.0,4,0.5323069393634796,0.7520566980044047,0.5722926259040833,0.693462073802948,0.5514254768689474,0.2662951275706291,28,0,12
25
+ 61a7d453-d120-440f-abfe-fa05714179b4.mp4,20.49268341064453,2.6226015090942383,87.20225430442176,91.52333333333333,28.0625,1.0,4,0.5853416124979655,0.6666666666666666,1.0,0.3333347989194711,2.981454431780764e-25,0.0,35,0,9
26
+ 64e41231-4092-46c3-906d-5170de857162.mp4,28.37565803527832,5.465407371520996,80.73909910837637,88.54666666666667,26.05208333333333,0.0,4,0.745102326075236,0.670194149017334,0.5261250138282776,0.7535956104596456,0.707586407661438,0.3179372052351634,33,0,7
27
+ 691de276-7b47-4015-bc54-da4343889084.mp4,21.35616779327393,3.5211429595947266,83.51229024945336,92.0,24.45,0.0,4,0.6291013558705648,0.647834857304891,0.6641747156778971,0.6670735081036886,0.7004674077033997,0.6992351412773132,32,0,11
28
+ 733704b4-12f7-4977-b920-6896bc9ca77c.mp4,42.62840366363525,5.25462818145752,87.67341084850415,83.07333333333332,25.05,-1.0,4,0.6398445963859558,0.6260692278544108,0.5751756628354391,0.6669502059618632,0.7081162532170614,0.6691677371660868,32,0,10
29
+ 81e87893-da5e-4db2-afd1-b1bb11a03de7.mp4,11.973058700561523,2.3520984649658203,80.35507447353024,93.7,28.125,2.0,4,7.679543605343628e-09,1.0,1.0,0.3358941231754215,4.8890841564280026e-21,4.577665144872662e-36,35,0,6
30
+ 890c5911-8ee1-4faa-a801-c605f0dde8e0.mp4,9.611075401306152,1.5621337890625,83.74652446436738,88.86,26.85483870967742,-1.0,4,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,34,0,11
31
+ 8b6d7852-7f21-47df-aa03-fc8c0f29e6d3.mp4,,,,,28.83,1.0,4,0.6386719544728597,0.6828855474789938,0.5790578722953796,0.6756150126457214,0.7062546610832214,0.6745621760686239,36,0,11
iqf_scores_3.csv ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ file_name,input_size,output_size,final_compression,final_score,ori_luma_qp,ori_chroma,ori_preset,brightness,contrast,colorfulness,sharpness,quality_factor,mos_zscore,luma_qp,chroma,preset
2
+ 04d651c3-3b08-4de9-befe-af12b3b3555b.mp4,11.63755702972412,3.1724796295166016,72.73929896615246,93.11666666666667,28.1,0.0,4,1.8469307366129406e-16,0.9999998410542806,1.0,0.9999999602635702,0.298225900199812,0.0,35,0,6
3
+ 07484eba-7a60-4754-ba0b-f9c0744af11b.mp4,50.56832885742188,9.012784957885742,82.17701640230703,88.19333333333333,26.691666666666663,-1.0,4,0.6979172627131144,0.6313334306081136,0.6084306836128235,0.6348755955696106,0.6509576241175333,0.7038862506548563,32,0,12
4
+ 0cb873d6-4c3a-4ced-84e9-87939b08565d.mp4,19.441584587097168,1.5586957931518557,91.98267103090808,93.29666666666668,26.13,1.0,4,2.1057123452742498e-07,0.9972970485687256,0.9999999602635702,0.9853918751080832,0.3318715903168057,6.486347245594561e-25,33,0,11
5
+ 0d0496e7-fe16-443b-9c16-369aa386bb4e.mp4,91.49424362182616,40.06210517883301,56.21352383170465,89.47666666666667,28.391666666666666,-1.0,4,0.5955032904942831,0.7397701740264893,0.5834726889928182,0.7210836410522461,0.65736456712087,0.4505663216114044,34,0,11
6
+ 0e4ce3ab-c7c1-4dd6-9d9d-983eabf8acf3.mp4,14.362677574157717,3.4272356033325195,76.13790614154684,88.80666666666666,25.425,-1.0,4,0.6425169706344604,0.6254289746284485,0.5407780408859253,0.6723130941390991,0.7194178899129232,0.6874635815620422,31,0,8
7
+ 10e3a5e7-dda1-496d-84bb-f826e6567d82.mp4,12.642633438110352,2.90421199798584,77.02842519161165,88.29,25.875,-1.0,4,0.666383703549703,0.644948422908783,0.5396574338277181,0.6548968553543091,0.6909757057825724,0.6799997289975485,31,0,8
8
+ 12cb34d6-c1d8-4b85-8f0e-60afaf26ffc9.mp4,,,,,31.183333333333337,-1.0,4,0.559302568435669,0.6554734110832214,0.517133355140686,0.6597744027773539,0.6716094811757406,0.6479589343070984,36,0,10
iqf_scores_4.csv ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ file_name,input_size,output_size,final_compression,final_score,ori_luma_qp,psnr_values,scene_type,luma_qp,brightness,contrast,colorfulness,sharpness,quality_factor,mos_zscore,chroma,preset
2
+ 07484eba-7a60-4754-ba0b-f9c0744af11b.mp4,50.56832885742188,34.3788480758667,0.6798494008532168,95.12666666666668,23.691666666666663,34.82451435257544,Sea,22,0.6979172627131144,0.6313334306081136,0.6084306836128235,0.6348755955696106,0.6509576241175333,0.7038862506548563,-1.0,6
3
+ 0d0496e7-fe16-443b-9c16-369aa386bb4e.mp4,91.49424362182616,100.9215087890625,1.1030367025733565,96.34999999999998,25.391666666666666,27.89417999534232,Sea,22,0.5955032904942831,0.7397701740264893,0.5834726889928182,0.7210836410522461,0.65736456712087,0.4505663216114044,-1.0,6
4
+ 0e4ce3ab-c7c1-4dd6-9d9d-983eabf8acf3.mp4,14.362677574157717,8.536499977111816,0.5943529632992148,95.10666666666668,22.425,30.44581862011659,Sea,22,0.6425169706344604,0.6254289746284485,0.5407780408859253,0.6723130941390991,0.7194178899129232,0.6874635815620422,-1.0,6
5
+ 10e3a5e7-dda1-496d-84bb-f826e6567d82.mp4,12.642633438110352,7.81199836730957,0.6179091093285072,94.66,22.875,29.992381826532565,Sea,22,0.666383703549703,0.644948422908783,0.5396574338277181,0.6548968553543091,0.6909757057825724,0.6799997289975485,-1.0,6
6
+ 12cb34d6-c1d8-4b85-8f0e-60afaf26ffc9.mp4,97.8766794204712,47.07356548309326,0.4809477166758856,99.79666666666668,28.183333333333334,16.3645023883341,Sea,32,0.559302568435669,0.6554734110832214,0.517133355140686,0.6597744027773539,0.6716094811757406,0.6479589343070984,-1.0,6
7
+ 12dd2d2b-24f3-4c31-b873-fa4e53803778.mp4,13.259980201721191,3.863698959350586,0.2913804470725427,96.45333333333332,19.0,31.048039674445505,Sea,22,0.0304905549855902,0.8879064122835795,0.9823326071103414,0.992975890636444,0.0004040396706827,2.798745185113234e-08,2.0,6
8
+ 221cab7a-eb1a-4b14-9568-9ac4a4ec9330.mp4,30.4854040145874,13.693915367126465,0.4491957974568376,95.35666666666668,25.975,24.29359649078312,Sea,32,0.6206316550572714,0.6937900980313619,0.5681640307108561,0.6865283250808716,0.6309727231661478,0.5269437432289124,-1.0,6
9
+ 249c9213-36ad-4280-a900-306b96685c0e.mp4,56.18430137634277,27.01200771331787,0.4807750038997846,92.53333333333336,22.06,35.30867793968724,Sea,22,0.6744417746861776,0.6287276744842529,0.5382514794667562,0.6795710523923238,0.7059144775072733,0.6740560928980509,-1.0,6
10
+ 2884fdc7-6b49-4008-ae33-54956784cf22.mp4,115.50566577911376,56.05314254760742,0.4852847881488354,99.82666666666668,26.975,19.456423450946858,Sea,32,0.5739724636077881,0.6645207007726034,0.5748539765675863,0.6702662507692972,0.6798134446144104,0.6453063488006592,-1.0,6
11
+ 39c98272-d26f-4139-afbf-d870b7553fab.mp4,11.697440147399902,7.938425064086914,0.678646350317207,94.16666666666669,22.95833333333333,30.240432260663724,Sea,22,0.6213184396425883,0.6266296108563741,0.5339837074279785,0.6631546020507812,0.7125220696131388,0.6837653716405233,-1.0,6
12
+ 424df265-1080-4edf-8cfc-b05f62acfffc.mp4,93.02153778076172,111.02602577209473,1.1935518205876898,96.40333333333336,25.941666666666663,26.716159386938628,Sea,22,0.572777271270752,0.8049134612083435,0.5622189939022064,0.7238964438438416,0.6895942687988281,0.4115677277247111,-1.0,6
13
+ 494e83e7-548e-408f-af3d-d702cc6ae594.mp4,6.668767929077148,3.722039222717285,0.5581299667796891,97.79,22.02083333333333,27.043169740053514,Sea,22,0.6418240666389465,0.6350036462148031,0.5903654098510742,0.6643276611963908,0.6858240564664205,0.6821937759717306,-1.0,6
14
+ 59bb4aa2-724b-490a-944c-eaae28496192.mp4,,,,,26.125,18.335617949324277,Sea,32,9.296274529428211e-07,0.3862022735666339,0.024743432644754648,1.0,1.0,0.017193238261976756,-1.0,6
iqf_scores_custom.csv ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ file_name,input_size,output_size,final_compression,final_score,ori_luma_qp,ori_chroma,ori_preset,psnr_values,scene_type,brightness,contrast,colorfulness,sharpness,quality_factor,mos_zscore,luma_qp,chroma,preset
2
+ 04d651c3-3b08-4de9-befe-af12b3b3555b.mp4,11.63755702972412,2.9998998641967773,74.22225423656728,89.54666666666667,24.1,0.0,4,24.34790082106915,Glacier,1.8469307366129406e-16,0.9999998410542806,1.0,0.9999999602635702,0.298225900199812,0.0,36,0.0,12
3
+ 07484eba-7a60-4754-ba0b-f9c0744af11b.mp4,50.56832885742188,5.350363731384277,89.41953619533344,85.96666666666668,23.691666666666663,-1.0,4,34.82451435257544,Sea,0.6979172627131144,0.6313334306081136,0.6084306836128235,0.6348755955696106,0.6509576241175333,0.7038862506548563,36,-1.0,12
4
+ 0cb873d6-4c3a-4ced-84e9-87939b08565d.mp4,19.441584587097168,0.6575107574462891,96.61801868823665,90.50666666666666,19.13,1.0,4,32.55061039813054,Buildings,2.1057123452742498e-07,0.9972970485687256,0.9999999602635702,0.9853918751080832,0.3318715903168057,6.486347245594561e-25,43,1.0,12
5
+ 0d0496e7-fe16-443b-9c16-369aa386bb4e.mp4,91.49424362182616,33.905229568481445,62.94277298075475,87.64666666666666,25.391666666666666,-1.0,4,27.89417999534232,Sea,0.5955032904942831,0.7397701740264893,0.5834726889928182,0.7210836410522461,0.65736456712087,0.4505663216114044,36,-1.0,12
6
+ 0e4ce3ab-c7c1-4dd6-9d9d-983eabf8acf3.mp4,14.362677574157717,3.847278594970703,73.21336098296196,88.19333333333333,22.425,-1.0,4,30.44581862011659,Sea,0.6425169706344604,0.6254289746284485,0.5407780408859253,0.6723130941390991,0.7194178899129232,0.6874635815620422,29,-1.0,12
7
+ 10e3a5e7-dda1-496d-84bb-f826e6567d82.mp4,12.642633438110352,3.197593688964844,74.70785098201205,86.99000000000001,22.875,-1.0,4,29.992381826532565,Sea,0.666383703549703,0.644948422908783,0.5396574338277181,0.6548968553543091,0.6909757057825724,0.6799997289975485,29,-1.0,12
8
+ 12cb34d6-c1d8-4b85-8f0e-60afaf26ffc9.mp4,97.8766794204712,13.43449592590332,86.27405832988092,87.71333333333332,28.183333333333334,-1.0,4,16.3645023883341,Sea,0.559302568435669,0.6554734110832214,0.517133355140686,0.6597744027773539,0.6716094811757406,0.6479589343070984,50,-1.0,12
9
+ 12dd2d2b-24f3-4c31-b873-fa4e53803778.mp4,13.259980201721191,0.6298971176147461,95.24963757085412,86.85333333333334,19.0,2.0,4,31.048039674445505,,0.0304905549855902,0.8879064122835795,0.9823326071103414,0.992975890636444,0.0004040396706827,2.798745185113234e-08,43,2.0,12
10
+ 221cab7a-eb1a-4b14-9568-9ac4a4ec9330.mp4,30.4854040145874,8.677053451538086,71.53702326731154,89.46666666666665,25.975,-1.0,4,24.29359649078312,Sea,0.6206316550572714,0.6937900980313619,0.5681640307108561,0.6865283250808716,0.6309727231661478,0.5269437432289124,36,-1.0,12
11
+ 249c9213-36ad-4280-a900-306b96685c0e.mp4,56.18430137634277,10.10262393951416,82.01877803580197,86.36,22.06,-1.0,4,35.30867793968724,Sea,0.6744417746861776,0.6287276744842529,0.5382514794667562,0.6795710523923238,0.7059144775072733,0.6740560928980509,29,-1.0,12
12
+ 2884fdc7-6b49-4008-ae33-54956784cf22.mp4,115.50566577911376,16.16676139831543,86.0034905740193,89.88666666666667,26.975,-1.0,4,19.456423450946858,Sea,0.5739724636077881,0.6645207007726034,0.5748539765675863,0.6702662507692972,0.6798134446144104,0.6453063488006592,50,-1.0,12
13
+ 2efd6ba4-b102-4009-b197-da0cdaa634dc.mp4,24.736478805541992,0.8398094177246094,96.60497589682628,90.75333333333332,21.04,0.0,4,17.135833803028024,Street,0.2123574561207135,0.923816998799642,0.5404039584100246,0.557942457186679,0.5570492148494531,0.1948199073473612,50,0.0,12
14
+ 31f7bc2c-838f-43c2-9cf8-745cc9ce27ab.mp4,22.826221466064453,1.5528364181518557,93.1971376845684,88.98333333333333,19.43,0.0,4,35.789955905092,Glacier,0.2436689063906669,0.865463117758433,0.5987633650268739,0.663530599674465,0.0083957752296021,0.0033863165364441,36,0.0,12
15
+ 329f1c4c-f5df-453d-9cfc-6f2cbef5142b.mp4,9.611075401306152,1.3005437850952148,86.46828028299029,87.64,19.85483870967742,-1.0,4,29.396985786067585,Buildings,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,36,-1.0,12
16
+ 34e7d8b9-5fd5-44d6-ba9c-194d4e2078ae.mp4,8.409784317016602,1.3808231353759766,83.58075447212148,91.11333333333334,21.86,0.0,4,27.697119754095937,Street,0.6085565884908041,0.7023536364237467,0.5811308026313782,0.6709411342938741,0.6804569562276205,0.6665542125701904,34,0.0,12
17
+ 39c98272-d26f-4139-afbf-d870b7553fab.mp4,11.697440147399902,3.302997589111328,71.7630733947759,85.51999999999998,22.95833333333333,-1.0,4,30.240432260663724,Sea,0.6213184396425883,0.6266296108563741,0.5339837074279785,0.6631546020507812,0.7125220696131388,0.6837653716405233,29,-1.0,12
18
+ 40949702-a809-4464-9d14-481e76abd967.mp4,11.222305297851562,3.0290184020996094,73.00894672078209,90.15666666666664,23.79,0.0,4,23.28805529599666,Street,3.0931066325599755e-11,0.6665612919865964,1.0,0.5694492856661543,0.013828640182813,4.3167463616559045e-17,41,0.0,12
19
+ 424df265-1080-4edf-8cfc-b05f62acfffc.mp4,93.02153778076172,36.84726619720459,60.38845725809408,87.91000000000001,25.941666666666663,-1.0,4,26.716159386938628,Sea,0.572777271270752,0.8049134612083435,0.5622189939022064,0.7238964438438416,0.6895942687988281,0.4115677277247111,36,-1.0,12
20
+ 4df1a23d-73f2-4cab-b43c-5538b566a458.mp4,9.611075401306152,1.3005437850952148,86.46828028299029,87.64,19.85483870967742,-1.0,4,29.396985786067585,Buildings,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,36,-1.0,12
21
+ 59bb4aa2-724b-490a-944c-eaae28496192.mp4,82.74820232391357,40.78819561004639,50.7080583450223,93.95,26.125,-1.0,4,18.33561794932428,Sea,9.296274529428212e-07,0.3862022735666339,0.0247434326447546,1.0,1.0,0.0171932382619767,36,-1.0,12
22
+ 5e800829-24b2-4ca7-bac4-89a02199fbec.mp4,9.155067443847656,2.107905387878418,76.97553403284907,89.20666666666666,21.866666666666667,1.0,4,36.65562661105682,Buildings,0.6375203331311544,0.6609386603037516,0.5363780458768209,0.6825437943140665,0.7010063727696737,0.6790463924407959,29,1.0,12
23
+ 5fe0ff99-37a3-4760-81a4-f441876cb867.mp4,9.611075401306152,1.3005437850952148,86.46828028299029,87.64,19.85483870967742,-1.0,4,29.396985786067585,Buildings,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,36,-1.0,12
24
+ 6084559b-4f79-480c-ae41-bdb3d0a6987c.mp4,8.81439208984375,0.2493619918823242,97.17096778381747,92.81,15.84375,2.0,4,44.19402395567592,,0.5323069393634796,0.7520566980044047,0.5722926259040833,0.693462073802948,0.5514254768689474,0.2662951275706291,36,2.0,12
25
+ 61a7d453-d120-440f-abfe-fa05714179b4.mp4,20.49268341064453,4.774928092956543,76.69935167945698,95.10333333333334,21.0625,1.0,4,23.28430139070841,Buildings,0.5853416124979655,0.6666666666666666,1.0,0.3333347989194711,2.981454431780764e-25,0.0,29,1.0,12
26
+ 64e41231-4092-46c3-906d-5170de857162.mp4,28.37565803527832,3.930176734924317,86.1494780842154,84.14333333333333,24.05208333333333,0.0,4,25.41429757462589,Mountain,0.745102326075236,0.670194149017334,0.5261250138282776,0.7535956104596456,0.707586407661438,0.3179372052351634,36,0.0,12
27
+ 691de276-7b47-4015-bc54-da4343889084.mp4,21.35616779327393,2.450800895690918,88.52415414874764,90.08333333333331,20.45,0.0,4,28.257029596777684,Glacier,0.6291013558705648,0.647834857304891,0.6641747156778971,0.6670735081036886,0.7004674077033997,0.6992351412773132,36,0.0,12
28
+ 733704b4-12f7-4977-b920-6896bc9ca77c.mp4,42.62840366363525,7.459269523620605,82.50164471914336,84.60666666666667,22.05,-1.0,4,34.503775113036255,Sea,0.6398445963859558,0.6260692278544108,0.5751756628354391,0.6669502059618632,0.7081162532170614,0.6691677371660868,29,-1.0,12
29
+ 81e87893-da5e-4db2-afd1-b1bb11a03de7.mp4,11.973058700561523,2.4130001068115234,79.846418804425,89.86666666666666,23.125,2.0,4,29.955702790992284,,7.679543605343628e-09,1.0,1.0,0.3358941231754215,4.8890841564280026e-21,4.577665144872662e-36,36,2.0,12
30
+ 890c5911-8ee1-4faa-a801-c605f0dde8e0.mp4,9.611075401306152,1.3005437850952148,86.46828028299029,87.64,19.85483870967742,-1.0,4,29.396985786067585,Buildings,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,36,-1.0,12
31
+ 8b6d7852-7f21-47df-aa03-fc8c0f29e6d3.mp4,36.639710426330566,2.526042938232422,93.1057235200824,89.26333333333334,21.83,1.0,4,19.31974385124029,Buildings,0.6386719544728597,0.6828855474789938,0.5790578722953796,0.6756150126457214,0.7062546610832214,0.6745621760686239,50,1.0,12
32
+ 8f0464f5-2e81-4d50-b4bb-0667fadd55b6.mp4,34.017616271972656,5.3361406326293945,84.31359625563806,74.34666666666668,27.291666666666668,0.0,4,17.875918400820858,Street,0.6737418174743652,0.6781927148501078,0.5551733573277792,0.6866800983746847,0.6682468851407369,0.6677144169807434,50,0.0,12
33
+ 93495912-fd6c-435a-9fcb-c9cc978c122e.mp4,14.999189376831056,1.5714950561523438,89.52280008825143,85.81,23.04201680672269,-1.0,4,29.72427086056156,Sea,0.3303557277324671,0.572416496075069,0.8744631012280782,0.5750492256289969,0.245182732741038,0.1620057622591654,36,-1.0,12
34
+ 9a049c4c-bb40-4692-8b95-cb2ca94d339f.mp4,7.705256462097168,0.5107479095458984,93.37143530447932,86.02666666666669,19.433333333333334,-1.0,4,47.35919096909639,Sea,0.5415121912956238,0.6919364531834921,0.5953385432561239,0.7154134511947632,0.6035712758700053,0.5871815880139669,36,-1.0,12
35
+ 9d860f37-78b5-4e91-bbff-91bbfaaaa32a.mp4,24.54138946533203,3.238698005676269,86.8031188280869,82.79,24.05208333333333,0.0,4,25.67234493498757,Mountain,0.5807816584904989,0.6518141428629557,0.5270594159762064,0.6739723483721415,0.6623684565226237,0.5438634157180786,36,0.0,12
36
+ a3fa72da-8a4e-466c-9a11-fc2a67863b9e.mp4,66.18983554840088,5.890133857727051,91.10115048794776,83.83333333333333,24.291666666666668,-1.0,4,16.2941854660188,Sea,0.2128963904734471,0.8745509584744772,0.8768628239631653,0.579653590063875,0.24392955436218,0.2292507886886597,50,-1.0,12
37
+ a508a47e-295b-4be0-ab3f-4b6478dacc65.mp4,150.44969272613525,66.06287097930908,56.08972688328196,89.68666666666667,28.57017543859649,-1.0,4,20.067948955885825,Sea,0.626129150390625,0.6703311999638876,0.562980075677236,0.6703017155329386,0.6834884285926819,0.6630960901578268,36,-1.0,12
38
+ a5f32e7e-a5f3-4a7e-b7fd-dc0fb551e243.mp4,27.75857830047608,8.555665016174316,69.17830256448119,90.31333333333332,25.991666666666667,-1.0,4,20.73713088892713,Sea,0.6408935189247131,0.7019673387209574,0.5994003017743429,0.701171875,0.6946845253308614,0.6745195984840393,36,-1.0,12
39
+ a75909eb-460b-4eb8-ab69-1f81413fde39.mp4,15.16309642791748,0.7881450653076172,94.80221556952888,85.72666666666667,19.17708333333333,1.0,4,30.9834009328239,Buildings,0.0452174662328464,0.6664746296834588,0.9997160037358602,0.662308836724454,0.2454252270120681,4.1359750212209746e-11,43,1.0,12
40
+ a8fb16e1-2992-4ab7-95b7-47d48938f465.mp4,56.77720260620117,10.663334846496582,81.21898516125214,87.81666666666666,22.03,-1.0,4,32.6959957036605,Sea,0.6491552790006002,0.6554122765858968,0.5638013680775961,0.663050651550293,0.7081366777420044,0.6996987660725912,29,-1.0,12
41
+ acfbb4ee-70e5-45bf-8aa8-349e388499ed.mp4,9.611075401306152,1.3005437850952148,86.46828028299029,87.64,19.85483870967742,-1.0,4,29.396985786067585,Buildings,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,36,-1.0,12
42
+ af18139c-acaf-4c43-977a-9a86fb3f3602.mp4,15.229940414428713,1.370335578918457,91.00235758230404,92.81666666666666,18.510416666666668,2.0,4,32.8864798429277,,0.2569143105560701,0.3665943775946895,0.7403472264607748,0.6492060224215189,0.3337878490177342,0.0003729414893314,36,2.0,12
43
+ b2b4e988-b71d-4d6f-a5dd-8db222348faa.mp4,8.236392974853516,1.0207548141479492,87.60677377506865,88.11666666666666,20.3,2.0,4,30.94902322197443,,0.6797566215197245,0.6921680370966593,0.5756566723187765,0.6673476894696554,0.7057122588157654,0.6777393817901611,36,2.0,12
44
+ b872dec9-a527-443e-807b-8a650fb44aa5.mp4,31.921814918518063,6.28378963470459,80.31506150028042,89.21666666666665,21.316666666666663,2.0,4,31.778037288875964,,0.5625234958715737,0.8364881674448649,0.8529369036356608,0.3062493354858209,0.2298784634719292,0.2218764863639043,29,2.0,12
45
+ bb85c24e-dfb2-4e74-a538-7b88444ba7a2.mp4,7.403351783752441,1.4399042129516602,80.55064442416871,86.10000000000001,21.10833333333333,-1.0,4,43.96922372729665,Sea,0.5719117522239685,0.5746606588363647,0.7491706609725952,0.6051486531893412,0.6949742039044698,0.6399241884549459,29,-1.0,12
46
+ bd22f174-e591-4d7a-a5fa-40447e18191e.mp4,14.803239822387695,2.4618701934814453,83.36938249316049,84.21,25.00438596491228,-1.0,4,30.95666927265913,Sea,0.3112848651896153,0.9546863834063212,0.6849623595674833,0.8972047170003256,0.5202026833430864,0.2215489234929057,36,-1.0,12
47
+ bdc6f64e-fe6c-4f98-b37d-a8405f0674a6.mp4,9.611075401306152,1.3005437850952148,86.46828028299029,87.64,19.85483870967742,-1.0,4,29.396985786067585,Buildings,0.583096981048584,0.6963175336519877,0.5527470707893372,0.6834075252215067,0.6812475522359213,0.5811437368392944,36,-1.0,12
48
+ c6b21693-9149-46a7-8062-b4e36d861374.mp4,6.766033172607422,0.555415153503418,91.79112576994093,89.67333333333333,19.1,0.0,4,49.603593158290806,Mountain,0.6675481796264648,0.6689058740933737,0.6905355652173361,0.6873549222946167,0.6984812021255493,0.6681686639785767,36,0.0,12
iqf_scores_video1.csv ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ file_name,input_size,output_size,final_compression,final_score,psnr_values,scene_type,luma_qp,quality_noise_level,quality_sharpness,quality_contrast,quality_brightness,quality_color_saturation,quality_motion_blur,quality_compression_artifacts,quality_text_content,quality_edge_density,quality_temporal_consistency,chroma,preset
2
+ 026fe674-d8c4-40a4-96c5-0cb6f1fb7c06.mp4,22.074841499328613,2.785863876342773,0.1262008552327546,93.08666666666666,26.13181026051079,Sea,33,0.0281049907207489,0.0167996753007173,0.1342756832257432,0.3967183437811308,0.3348408286037159,0.0102033827781422,0.0042888157380123,0.2226463602789319,0.0173612439864421,0.9997948641607676,2,8
3
+ 1fda2da4-b877-415b-a0b2-9643e8c19343.mp4,42.13912010192871,4.191298484802246,0.0994633602852663,88.55666666666667,19.74997773179496,Mountain,36,0.1175154820084571,0.0461746454238891,0.1698552824803696,0.4773372729081188,0.1762627786880638,0.0413492260696467,0.0111680872117479,0.7232348793690635,0.0703951503710317,0.9971271971765552,1,7
4
+ 223c0d7d-c60b-4ce0-b867-a6292c9aa465.mp4,45.65006923675537,4.773685455322266,0.1045712642967627,90.91,18.361035670056648,Mountain,35,0.1735588163137436,0.0710327699780464,0.1924423491662839,0.3859789412249714,0.1846702573626785,0.0851046132594358,0.015286094819506,0.9235667442983528,0.1461225657623207,0.991769307651044,1,6
5
+ 2b4b64a6-a07f-4bce-93eb-0b3ce53e0575.mp4,45.77764892578125,4.4001970291137695,0.0961210794431089,89.87333333333333,20.31808044588177,Mountain,35,0.1654309034347534,0.0693681240081787,0.1945907355813904,0.4009358367907413,0.1967351534458278,0.0817902631896655,0.0148024419322609,0.9143815857079082,0.1401244914633221,0.9930504353683388,1,6
6
+ 2b8983b8-2396-4020-a6db-35b19bb2d287.mp4,47.75133895874024,4.317131996154785,0.0904086061311291,88.83666666666666,20.24408139521997,Mountain,38,0.1422079205513,0.0581630282104015,0.2012230718832824,0.3787347649628497,0.2031581901056525,0.073356599381059,0.0149929175774256,0.7752869102153293,0.1257005266287348,0.9969557560560646,1,7
7
+ 43a9cd6b-81bc-438d-819c-6352d7193145.mp4,43.062859535217285,4.463125228881836,0.1036421008045654,88.68333333333334,19.1756830753702,Mountain,35,0.1361274570226669,0.0552828311920166,0.1703905049275502,0.4539282307889309,0.1831038004086405,0.0580689047895802,0.0123649441326657,0.731457977111989,0.1049097084179139,0.9815015737117856,1,7
8
+ 494e83e7-548e-408f-af3d-d702cc6ae594.mp4,6.668767929077148,1.1391572952270508,0.1708197537149402,92.08666666666666,15.28414787432056,Sea,36,0.0644450113177299,0.0318450145423412,0.1218587687048134,0.5155074374479302,0.3446948237605802,0.0206735079199236,0.0081399170060952,0.3944745150014188,0.0389192013002089,0.9925388100348878,2,7
9
+ 5533624b-8b2f-476e-93c3-26ecc8abbbfc.mp4,48.74678897857666,4.002272605895996,0.0821033075153919,88.94,20.368462730755084,Mountain,37,0.1413482576608657,0.0594401359558105,0.1886964347338915,0.4077637824619008,0.2283467568483899,0.0767561909194802,0.0143880064909656,0.6034597344262048,0.1327680358778163,0.9937148534950766,1,7
10
+ 58628666-a50b-4b53-86f3-9bce954fd87a.mp4,17.49576473236084,4.228129386901856,0.2416658803762573,98.91666666666669,21.25486157432972,Sea,33,0.0431025587022304,0.0199701022356748,0.3801464858933288,0.1332456478568448,0.611579316008798,0.0024631645124081,0.0025733727961778,0.6121459220339367,0.0034776776627143,0.9973595402976382,2,8
11
+ 5cbb381b-c803-4c00-9f7c-eb34a93f0f11.mp4,43.69903087615967,4.624711036682129,0.1058309748284412,89.24666666666667,17.629037236788477,Mountain,35,0.1505059599876403,0.0603656210005283,0.1814356638339386,0.4207115634726497,0.1782449488412061,0.0653040586070096,0.0135400975123047,0.8411135233554857,0.119503881935261,0.9788613655686916,1,7
12
+ 6f22e710-af89-4704-8e14-022222e9748b.mp4,26.640109062194824,2.7298479080200195,0.1024713488089268,92.90000000000002,25.658407466420066,Sea,38,0.0485589206218719,0.0231323186308145,0.1153739397145537,0.463199056968312,0.3847542499002731,0.0137213119667827,0.0031315109226852,0.1833413000403856,0.0216421512948659,0.9949059255782428,2,8
13
+ 708b5e08-9d59-4b41-9dba-f7735682a9b4.mp4,27.11849975585937,3.5335893630981445,0.1303018011656289,88.83666666666666,19.987420021489257,Glacier,39,0.0166761334985494,0.0071951509453356,0.2228156889157377,0.3924565241366342,0.7564402887392186,0.0021965440270935,0.0035960952906558,0.5808692813811348,0.0034892198617952,0.9858591746085408,2,8
14
+ 70b2de9e-3c32-4b01-8505-8a78d5bccd59.mp4,37.72974681854248,6.375006675720215,0.1689650001199368,90.67,26.28220540391436,Sea,33,0.0360238887369632,0.0152332307770848,0.1206950385830347,0.511027495028049,0.2998014956295594,0.0078212501433094,0.0043184760337074,0.282369536052244,0.0135870261694049,0.9988060480115136,2,8
15
+ 7fb26377-5336-45c3-8648-14c7df0d1822.mp4,8.315752983093262,1.3558111190795898,0.1630412930538084,94.14333333333332,18.98384660344204,Glacier,35,0.0722586363554,0.0439577437937259,0.1263059475750047,0.5686536038260235,0.377071315116488,0.0240558069117096,0.009015806329747,0.5724709479049758,0.0389493897306397,0.9592674120012764,2,7
16
+ 814a4743-06ee-4a4b-9e77-e11bee77ceeb.mp4,5.243513107299805,0.6225442886352539,0.1187265628779631,93.65333333333332,22.36412702479837,Glacier,34,0.0474878959357738,0.0205531790852546,0.1985460133073029,0.2297653506247488,0.7212429583505275,0.010479251782424,0.0074468950430552,0.7969974880122681,0.0183791027225598,0.9985523841836662,1,8
17
+ 8fb42072-b53c-4ee3-b54d-4c2e1cf6d1ea.mp4,27.705836296081543,9.31836223602295,0.33633210477537,92.65666666666668,15.691813835264224,Street,33,0.4326435327529907,0.1827086210250854,0.1589946302332441,0.4811455634902676,0.2646468814980862,0.1195052192903368,0.0232531875371933,0.484191548456252,0.1929814757768828,0.9696954825913656,1,6
18
+ 918a9d77-727b-4255-bf27-d67c73ab109e.mp4,53.68707656860352,12.61237907409668,0.2349239310503353,95.35,13.299115682322263,Buildings,33,0.1324290484189987,0.0732438713312149,0.1579082716568159,0.2904904572709194,0.4459472019345969,0.0440004614111756,0.0098852856705586,0.3789205217776646,0.077397912777377,0.977604551435959,1,6
19
+ b7340fda-e72e-4b12-9029-316dbd0c9ce8.mp4,16.855341911315918,1.5283565521240234,0.0906749065172005,93.06333333333332,30.707378416224845,Buildings,39,0.075921282172203,0.034921821206808,0.1509332493571397,0.3837449406136722,0.3531127043688376,0.0164538663940422,0.0059778722934424,0.0796282384863345,0.018695976111073,0.961157444517792,2,8
20
+ be4a76cd-c69a-433b-b55c-9dfd478c9594.mp4,91.5072259902954,20.27893829345703,0.2216102397815848,94.58333333333331,13.14202322363013,Buildings,33,0.2657359540462494,0.1347031891345977,0.1066279604487404,0.4325665933039882,0.5377423675610595,0.0564917502088554,0.0161084520320097,0.1908051378446115,0.097688081811672,0.9839293682385236,1,6
21
+ c7d92eab-29ca-490c-ab85-e18229efdf96.mp4,97.28178691864014,33.901222229003906,0.3484847811991425,94.10333333333334,13.507726959867174,Sea,33,0.192488357424736,0.1012503281235694,0.0961965353301766,0.4876347176471124,0.2784021018853362,0.0490402666342134,0.0148710856835047,0.1341335683256892,0.0872538335327444,0.98749093327581,2,6
22
+ d4346f5c-0084-437f-a2ab-3eaedfc55c11.mp4,21.600625038146973,2.7130613327026367,0.1256010568171677,86.63333333333333,29.301524936051145,Mountain,35,0.0324001275002956,0.0119790434837341,0.0899387215730463,0.4335584335421187,0.3625504576623417,0.0055937685794973,0.0050580248547097,0.1096177840090553,0.0100087439759552,0.9993855382951184,2,8
23
+ d69e637f-ced7-4a94-b11f-7e3a7fc584c5.mp4,42.82363224029541,7.869690895080566,0.1837698131471316,88.47000000000001,21.705490783403683,Sea,38,0.0484559424221515,0.0249015484005212,0.1273258361643435,0.435330105384125,0.4183141918787222,0.0173588259087107,0.0059984366719921,0.2724476860040493,0.0288331306644454,0.99976134757579,2,8
24
+ da4c28f0-dce4-49b6-b992-df633c640d41.mp4,127.13426303863524,33.8479118347168,0.2662375273645205,98.95,14.78405775820291,Forest,33,0.6543295979499817,0.2910408973693847,0.1373758221723152,0.5046602271961581,0.3814980234424291,0.1978286082474226,0.0325104215492804,0.4851184786004373,0.2947656396438612,0.9992652571806604,2,6
25
+ dcc6663d-5979-4b94-8780-8f08f1459367.mp4,54.03766345977783,16.396857261657715,0.3034338683770529,94.12333333333332,23.759774352151624,Street,33,0.370427519083023,0.2039624601602554,0.133535066310104,0.339642842742698,0.3444164673070249,0.1023351721047729,0.0232562640060981,0.3504134883616999,0.1369291179189364,0.9939171225978608,1,6
26
+ e2dd775b-6dbe-49fd-876e-3e9b4a2e80cd.mp4,52.91536045074463,9.409367561340332,0.1778192094164961,93.73666666666666,13.593667907575028,Buildings,36,0.1354481726884842,0.0743122622370719,0.1634576248055144,0.3116403942588515,0.4550657866970771,0.0439209715183308,0.0097172350312272,0.1103883875206228,0.0768300366208766,0.9770803141912644,1,6
27
+ ea63ccc6-22fa-4b9b-91eb-67da4241ed6d.mp4,51.6378231048584,11.61158561706543,0.2248659009789462,94.10333333333334,10.662733148504527,Buildings,35,0.0870254561305046,0.0478470772504806,0.1353242805091566,0.4523556977557734,0.3012495941855623,0.0307206434675966,0.008035152995338,0.143403323368975,0.0540664242644898,0.9066783540598498,2,6
28
+ f0bab96c-7918-40b7-8e75-470ee67f2ce4.mp4,50.66206169128418,19.06877613067627,0.3763916329910599,93.69666666666669,22.65985438616542,Sea,31,0.138214036822319,0.0643599405884742,0.4485925112259545,0.0523797637873839,0.3629621079527614,0.0136145792224255,0.0061063171985248,0.8541742905320562,0.0238152737416137,0.9990574532561204,2,8
29
+ f67fdc5d-c16c-434f-88b6-2885b6e16244.mp4,4.923687934875488,0.8116235733032227,0.16484057967084526,95.21,12.12852561266281,Street,37,0.037277165800333,0.0159068945795297,0.1130266998501148,0.5849823456829463,0.2362355845124187,0.0032554956997445,0.005870988437285,0.3444003885611866,0.0038838354199269,0.9677222326473224,1,7
neurons/miner.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import uuid
3
+ import traceback
4
+ import os
5
+ from typing import Tuple
6
+ from loguru import logger
7
+ import bittensor as bt
8
+ from vidaio_subnet_core.base.miner import BaseMiner
9
+ from vidaio_subnet_core.protocol import VideoUpscalingProtocol, LengthCheckProtocol, ContentLength, VideoCompressionProtocol, TaskWarrantProtocol, TaskType
10
+ from services.miner_utilities.miner_utils import video_upscaler, video_compressor
11
+
12
+ from vidaio_subnet_core.utilities.version import check_version
13
+
14
+ MAX_CONTENT_LEN = ContentLength.FIVE
15
+ warrant_task = TaskType.COMPRESSION
16
+
17
+ VALIDATOR_HOTKEYS = [
18
+ "5FR392L6eKrTGwUjQurpxw89YUe7LzKANktDwRKLYYSgwxhb",
19
+ "5E2LP6EnZ54m3wS8s1yPvD5c3xo71kQroBw7aUVK32TKeZ5u",
20
+ "5CyiseaJEUjRpE1nLLYZBNk3Akty4yugUZSyBg8H4BfRsn85",
21
+ "5EUqqYFhNeYbZCm6UTQ3SULNDibNs2ZVjukV8pqeYZA6ms85"
22
+ ]
23
+
24
+ class Miner(BaseMiner):
25
+ def __init__(self, config: dict | None = None) -> None:
26
+ """
27
+ Initializes the Miner instance.
28
+ """
29
+ super().__init__()
30
+
31
+ async def forward_upscaling_requests(self, synapse: VideoUpscalingProtocol) -> VideoUpscalingProtocol:
32
+ """
33
+ Processes a video upscaling request by downloading, upscaling,
34
+ uploading, and returning a sharing link.
35
+ """
36
+
37
+ should_blacklist, reason = self.blacklist_all_requests(synapse)
38
+ if should_blacklist:
39
+ logger.warning(f"Upscaling Request blacklisted: {reason}")
40
+ return synapse
41
+
42
+ start_time = time.time()
43
+
44
+ task_type: str = synapse.miner_payload.task_type
45
+ payload_url: str = synapse.miner_payload.reference_video_url
46
+ validator_uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
47
+
48
+ logger.info(f"✅✅✅ Receiving {task_type} Request from validator: {synapse.dendrite.hotkey} with uid: {validator_uid}: round_id : {synapse.round_id}")
49
+
50
+ check_version(synapse.version)
51
+
52
+ try:
53
+ processed_video_url = await video_upscaler(payload_url, task_type)
54
+
55
+ if processed_video_url is None:
56
+ logger.info(f"💔 Failed to upscaling video 💔")
57
+ return synapse
58
+
59
+ synapse.miner_response.optimized_video_url = processed_video_url
60
+
61
+ processed_time = time.time() - start_time
62
+
63
+ logger.info(f"💜 Returning Response, Processed in {processed_time:.2f} seconds 💜")
64
+
65
+ return synapse
66
+
67
+ except Exception as e:
68
+ logger.error(f"Failed to process upscaling request: {e}")
69
+ traceback.print_exc()
70
+ return synapse
71
+
72
+ async def forward_compression_requests(self, synapse: VideoCompressionProtocol) -> VideoCompressionProtocol:
73
+ """
74
+ Processes a video compression request by downloading, compressing,
75
+ uploading, and returning a sharing link.
76
+ """
77
+ should_blacklist, reason = self.blacklist_all_requests(synapse)
78
+ if should_blacklist:
79
+ logger.warning(f"Compression Request blacklisted: {reason}")
80
+ return synapse
81
+
82
+ start_time = time.time()
83
+
84
+ payload_url: str = synapse.miner_payload.reference_video_url
85
+ vmaf_threshold: float = synapse.miner_payload.vmaf_threshold
86
+ validator_uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
87
+
88
+ logger.info(f"🛜🛜🛜 Receiving CompressionRequest from validator: {synapse.dendrite.hotkey} with uid: {validator_uid} 🛜🛜🛜")
89
+ logger.info(f"Compression Parameters - VMAF Threshold: {vmaf_threshold}, Payload URL: {payload_url}")
90
+
91
+ check_version(synapse.version)
92
+
93
+ try:
94
+ processed_video_url = await video_compressor(payload_url, vmaf_threshold)
95
+
96
+ if processed_video_url is None:
97
+ logger.info(f"💔 Failed to compress video 💔")
98
+ return synapse
99
+
100
+ synapse.miner_response.optimized_video_url = processed_video_url
101
+
102
+ processed_time = time.time() - start_time
103
+
104
+ logger.info(f"💜 Returning Response, Processed in {processed_time:.2f} seconds 💜\n"
105
+ f"✅ Video compressed successfully. Optimized video URL: {processed_video_url}")
106
+
107
+ return synapse
108
+
109
+ except Exception as e:
110
+ logger.error(f"Failed to process compression request: {e}")
111
+ traceback.print_exc()
112
+ return synapse
113
+
114
+ async def forward_length_check_requests(self, synapse: LengthCheckProtocol) -> LengthCheckProtocol:
115
+
116
+ validator_uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
117
+ logger.info(f"⭐️⭐️⭐️ Receiving LengthCheckRequest from validator: {synapse.dendrite.hotkey} with uid: {validator_uid} ⭐️⭐️⭐️")
118
+
119
+ check_version(synapse.version)
120
+
121
+ synapse.max_content_length = MAX_CONTENT_LEN
122
+
123
+ return synapse
124
+
125
+ async def forward_task_warrant_requests(self, synapse: TaskWarrantProtocol) -> TaskWarrantProtocol:
126
+ """
127
+ Processes a task warrant request by verifying the task type and returning a warrant.
128
+ """
129
+
130
+ validator_uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
131
+ logger.info(f"🌕🌕🌕 Receiving TaskWarrantRequest from validator: {synapse.dendrite.hotkey} with uid: {validator_uid} 🌕🌕🌕")
132
+
133
+ check_version(synapse.version)
134
+
135
+ synapse.warrant_task = warrant_task
136
+
137
+ return synapse
138
+
139
+ async def blacklist_upscaling_requests(self, synapse: VideoUpscalingProtocol) -> Tuple[bool, str]:
140
+ """
141
+ Determines whether a request should be blacklisted based on the hotkey status.
142
+ """
143
+ if not synapse.dendrite or not synapse.dendrite.hotkey:
144
+ logger.warning("Received a request without a dendrite or hotkey.")
145
+ return True, "Missing dendrite or hotkey"
146
+
147
+ uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
148
+
149
+ if not self.metagraph.validator_permit[uid]:
150
+ logger.warning(f"Blacklisting non-validator hotkey {synapse.dendrite.hotkey}")
151
+ return True, "Non-validator hotkey"
152
+
153
+ logger.trace(f"Hotkey {synapse.dendrite.hotkey} recognized and allowed.")
154
+ return False, "Hotkey recognized!"
155
+
156
+ async def priority_upscaling_requests(self, synapse: VideoUpscalingProtocol) -> float:
157
+ """
158
+ Assigns a priority to requests based on the stake of the requesting entity.
159
+ Higher stakes result in higher priority.
160
+ """
161
+ if not synapse.dendrite or not synapse.dendrite.hotkey:
162
+ logger.warning("Received a request without a dendrite or hotkey.")
163
+ return 0.0
164
+
165
+ caller_uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
166
+ priority: float = float(self.metagraph.S[caller_uid])
167
+
168
+ logger.trace(f"Prioritizing {synapse.dendrite.hotkey} with value: {priority}")
169
+ return priority
170
+
171
+ async def blacklist_length_check_requests(self, synapse: LengthCheckProtocol) -> Tuple[bool, str]:
172
+ """
173
+ Determines whether a request should be blacklisted based on the hotkey status.
174
+ """
175
+ if not synapse.dendrite or not synapse.dendrite.hotkey:
176
+ logger.warning("Received a request without a dendrite or hotkey.")
177
+ return True, "Missing dendrite or hotkey"
178
+
179
+ uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
180
+
181
+ if not self.metagraph.validator_permit[uid]:
182
+ logger.warning(f"Blacklisting non-validator hotkey {synapse.dendrite.hotkey}")
183
+ return True, "Non-validator hotkey"
184
+
185
+ logger.trace(f"Hotkey {synapse.dendrite.hotkey} recognized and allowed.")
186
+ return False, "Hotkey recognized!"
187
+
188
+ async def priority_length_check_requests(self, synapse: LengthCheckProtocol) -> float:
189
+ """
190
+ Assigns a priority to requests based on the stake of the requesting entity.
191
+ Higher stakes result in higher priority.
192
+ """
193
+ if not synapse.dendrite or not synapse.dendrite.hotkey:
194
+ logger.warning("Received a request without a dendrite or hotkey.")
195
+ return 0.0
196
+
197
+ caller_uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
198
+ priority: float = float(self.metagraph.S[caller_uid])
199
+
200
+ logger.trace(f"Prioritizing {synapse.dendrite.hotkey} with value: {priority}")
201
+ return priority
202
+
203
+ async def blacklist_compression_requests(self, synapse: VideoCompressionProtocol) -> Tuple[bool, str]:
204
+ """
205
+ Determines whether a request should be blacklisted based on the hotkey status.
206
+ """
207
+ if not synapse.dendrite or not synapse.dendrite.hotkey:
208
+ logger.warning("Received a request without a dendrite or hotkey.")
209
+ return True, "Missing dendrite or hotkey"
210
+
211
+ uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
212
+
213
+ if not self.metagraph.validator_permit[uid]:
214
+ logger.warning(f"Blacklisting non-validator hotkey {synapse.dendrite.hotkey}")
215
+ return True, "Non-validator hotkey"
216
+
217
+ logger.trace(f"Hotkey {synapse.dendrite.hotkey} recognized and allowed.")
218
+ return False, "Hotkey recognized!"
219
+
220
+ async def priority_compression_requests(self, synapse: VideoCompressionProtocol) -> float:
221
+ """
222
+ Assigns a priority to requests based on the stake of the requesting entity.
223
+ Higher stakes result in higher priority.
224
+ """
225
+ if not synapse.dendrite or not synapse.dendrite.hotkey:
226
+ logger.warning("Received a request without a dendrite or hotkey.")
227
+ return 0.0
228
+
229
+ caller_uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
230
+ priority: float = float(self.metagraph.S[caller_uid])
231
+
232
+ logger.trace(f"Prioritizing {synapse.dendrite.hotkey} with value: {priority}")
233
+ return priority
234
+
235
+ async def blacklist_task_warrant_requests(self, synapse: TaskWarrantProtocol) -> Tuple[bool, str]:
236
+ """
237
+ Determines whether a request should be blacklisted based on the hotkey status.
238
+ """
239
+ if not synapse.dendrite or not synapse.dendrite.hotkey:
240
+ logger.warning("Received a request without a dendrite or hotkey.")
241
+ return True, "Missing dendrite or hotkey"
242
+
243
+ uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
244
+
245
+ if not self.metagraph.validator_permit[uid]:
246
+ logger.warning(f"Blacklisting non-validator hotkey {synapse.dendrite.hotkey}")
247
+ return True, "Non-validator hotkey"
248
+
249
+ logger.trace(f"Hotkey {synapse.dendrite.hotkey} recognized and allowed.")
250
+ return False, "Hotkey recognized!"
251
+
252
+ async def priority_task_warrant_requests(self, synapse: TaskWarrantProtocol) -> float:
253
+ """
254
+ Assigns a priority to requests based on the stake of the requesting entity.
255
+ Higher stakes result in higher priority.
256
+ """
257
+ if not synapse.dendrite or not synapse.dendrite.hotkey:
258
+ logger.warning("Received a request without a dendrite or hotkey.")
259
+ return 0.0
260
+
261
+ caller_uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
262
+ priority: float = float(self.metagraph.S[caller_uid])
263
+
264
+ logger.trace(f"Prioritizing {synapse.dendrite.hotkey} with value: {priority}")
265
+ return priority
266
+
267
+ def blacklist_all_requests(self, synapse) -> Tuple[bool, str]:
268
+ """
269
+ Determines whether a request should be blacklisted based on the hotkey status.
270
+ """
271
+ if not synapse.dendrite or not synapse.dendrite.hotkey:
272
+ logger.warning("Received a request without a dendrite or hotkey.")
273
+ return True, "Missing dendrite or hotkey"
274
+
275
+ uid: int = self.metagraph.hotkeys.index(synapse.dendrite.hotkey)
276
+
277
+ if not self.metagraph.validator_permit[uid]:
278
+ logger.warning(f"Blacklisting non-validator hotkey {synapse.dendrite.hotkey}")
279
+ return True, "Non-validator hotkey"
280
+
281
+ hotkey: str = synapse.dendrite.hotkey
282
+ if hotkey not in VALIDATOR_HOTKEYS:
283
+ logger.warning(f"Validator hotkey {hotkey} is not recognized.")
284
+ return True, "Validator hotkey not recognized."
285
+
286
+ if hotkey != VALIDATOR_HOTKEYS[0]:
287
+ logger.warning(f"Validator hotkey {hotkey} is not authorized to make requests at this time because of staking amount.",
288
+ f" Only {VALIDATOR_HOTKEYS[0]} is authorized.",
289
+ f" Request denied.")
290
+ return True, "Validator hotkey not authorized."
291
+
292
+ logger.trace(f"Hotkey {synapse.dendrite.hotkey} recognized and allowed.")
293
+ return False, "Hotkey recognized!"
294
+
295
+ if __name__ == "__main__":
296
+ with Miner() as miner:
297
+ while True:
298
+ logger.info(f"Miner running... {time.time()}")
299
+ time.sleep(50)
neurons/validator.py ADDED
@@ -0,0 +1,1227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import uuid
4
+ import httpx
5
+ import random
6
+ import asyncio
7
+ import traceback
8
+ import pandas as pd
9
+ import bittensor as bt
10
+ from loguru import logger
11
+ from datetime import datetime, timezone
12
+ from concurrent.futures import ThreadPoolExecutor
13
+ from vidaio_subnet_core.utilities.version import get_version
14
+ from vidaio_subnet_core import validating, CONFIG, base, protocol
15
+ from vidaio_subnet_core.utilities.wandb_manager import WandbManager
16
+ from services.video_scheduler.video_utils import get_trim_video_path
17
+ from vidaio_subnet_core.utilities.uids import get_organic_forward_uids
18
+ from vidaio_subnet_core.protocol import LengthCheckProtocol, TaskWarrantProtocol, TaskType
19
+ from vidaio_subnet_core.validating.managing.sql_schemas import MinerMetadata, MinerPerformanceHistory, Base
20
+ from sqlalchemy import desc, asc, func, select, delete
21
+ from sqlalchemy.ext.asyncio import AsyncSession
22
+ from datetime import datetime, timedelta
23
+ from services.dashboard.server import send_upscaling_data_to_dashboard, send_compression_data_to_dashboard
24
+ from services.video_scheduler.redis_utils import (
25
+ get_redis_connection,
26
+ get_organic_upscaling_queue_size,
27
+ get_organic_compression_queue_size,
28
+ set_scheduler_ready
29
+ )
30
+ from typing import List, Tuple, Any
31
+
32
+ VMAF_QUALITY_THRESHOLDS = [
33
+ 85, #Low
34
+ 90, #Medium
35
+ 95, #High
36
+ ]
37
+
38
+ SLEEP_TIME_LOW = 60 * 3 # 3 minutes
39
+ SLEEP_TIME_HIGH = 60 * 4 # 4 minutes
40
+
41
+ class Validator(base.BaseValidator):
42
+ def __init__(self):
43
+ super().__init__()
44
+ self.miner_manager = validating.managing.MinerManager(
45
+ uid=self.uid, config=self.config, wallet=self.wallet, metagraph=self.metagraph
46
+ )
47
+ logger.info("💧 Initialized miner manager 💧")
48
+
49
+ self.challenge_synthesizer = validating.synthesizing.Synthesizer()
50
+ logger.info("💧 Initialized challenge synthesizer 💧")
51
+
52
+ self.dendrite = bt.dendrite(wallet=self.wallet)
53
+ logger.info("💧 Initialized dendrite 💧")
54
+
55
+ self.score_client = httpx.AsyncClient(
56
+ base_url=f"http://{CONFIG.score.host}:{CONFIG.score.port}"
57
+ )
58
+ logger.info(
59
+ f"💧 Initialized score client with base URL: http://{CONFIG.score.host}:{CONFIG.score.port} 💧"
60
+ )
61
+
62
+ self.set_weights_executor = ThreadPoolExecutor(max_workers=1)
63
+ logger.info("💙 Initialized setting weights executor 💙")
64
+
65
+ self.redis_conn = get_redis_connection()
66
+ logger.info("💙 Initialized Redis connection 💙")
67
+
68
+ self.wandb_manager = WandbManager(validator=self)
69
+ logger.info("🔑 Initialized Wandb Manager 🔑")
70
+
71
+ self.organic_gateway_base_url = f"http://localhost:{CONFIG.organic_gateway.port}"
72
+
73
+ self.push_result_endpoint = f"http://{CONFIG.video_scheduler.host}:{CONFIG.video_scheduler.port}/api/push_result"
74
+
75
+ self.scheduler_ready_endpoint = f"http://{CONFIG.video_scheduler.host}:{CONFIG.video_scheduler.port}/api/scheduler_ready"
76
+
77
+ async def check_scheduler_ready(self) -> bool:
78
+ """
79
+ Check if the video scheduler is ready to process synthetic requests.
80
+
81
+ Returns:
82
+ bool: True if scheduler is ready, False otherwise
83
+ """
84
+ try:
85
+ async with httpx.AsyncClient() as client:
86
+ response = await client.get(self.scheduler_ready_endpoint, timeout=10)
87
+ response.raise_for_status()
88
+ data = response.json()
89
+ return data.get("ready", False)
90
+ except httpx.HTTPStatusError as e:
91
+ logger.error(f"HTTP error checking scheduler readiness: {e.response.status_code}")
92
+ return False
93
+ except httpx.RequestError as e:
94
+ logger.error(f"Request error checking scheduler readiness: {e}")
95
+ return False
96
+ except Exception as e:
97
+ logger.error(f"Unexpected error checking scheduler readiness: {e}")
98
+ return False
99
+
100
+ async def wait_for_scheduler_ready(self) -> None:
101
+ """
102
+ Wait for the scheduler to be ready before proceeding with synthetic requests.
103
+ """
104
+ logger.info("🔄 Checking if video scheduler is ready...")
105
+
106
+ while not await self.check_scheduler_ready():
107
+ logger.info("⏳ Waiting for scheduler server to be ready (all synthetic queues need to be populated)...")
108
+ await asyncio.sleep(10)
109
+
110
+ logger.info("✅ Scheduler is ready! Proceeding with synthetic requests.")
111
+
112
+ async def refresh_miner_manager(self, miner_uids: list[int]):
113
+ if not miner_uids:
114
+ return
115
+
116
+ miner_hotkeys = [self.metagraph.hotkeys[uid] for uid in miner_uids]
117
+
118
+ # Single query to fetch all miners
119
+ stmt = select(MinerMetadata).where(MinerMetadata.uid.in_(miner_uids))
120
+ result = self.miner_manager.session.execute(stmt)
121
+ miners = {miner.uid: miner for miner in result.scalars().all()}
122
+
123
+ delete_uids = []
124
+
125
+ for uid, latest_hotkey in zip(miner_uids, miner_hotkeys):
126
+ miner = miners.get(uid)
127
+ if miner is None:
128
+ continue
129
+
130
+ if miner.hotkey != latest_hotkey:
131
+ delete_uids.append(uid)
132
+ logger.info(
133
+ f"UID {uid} hotkey changed from {miner.hotkey} → {latest_hotkey}"
134
+ )
135
+ miner.hotkey = latest_hotkey # update record in-memory
136
+
137
+ if delete_uids:
138
+ delete_stmt = delete(MinerPerformanceHistory).where(
139
+ MinerPerformanceHistory.uid.in_(delete_uids)
140
+ )
141
+ self.miner_manager.session.execute(delete_stmt)
142
+
143
+ self.miner_manager.session.commit()
144
+
145
+ async def start_synthetic_epoch(self):
146
+ # Wait for scheduler to be ready first
147
+ await self.wait_for_scheduler_ready()
148
+
149
+ logger.info("✅✅✅✅✅ Starting synthetic forward ✅✅✅✅✅")
150
+ epoch_start_time = time.time()
151
+
152
+ miner_uids = self.filter_miners()
153
+ logger.debug(f"Initialized {len(miner_uids)} subnet neurons of total {len(self.metagraph.S)} neurons")
154
+
155
+ uids = self.miner_manager.consume(miner_uids)
156
+
157
+ logger.info(f"Filtered UIDs after consumption: {uids}")
158
+
159
+ random_uids = uids.copy()
160
+ random.shuffle(random_uids)
161
+ logger.info(f"Randomized UIDs: {random_uids}")
162
+
163
+ axons = [self.metagraph.axons[uid] for uid in random_uids]
164
+
165
+ logger.info(f"Sending TaskWarrantProtocol requests to {len(axons)} miners")
166
+
167
+ version = get_version()
168
+ task_warrant_synapse = TaskWarrantProtocol(version=version)
169
+
170
+ task_warrant_responses = await self.dendrite.forward(
171
+ axons=axons, synapse=task_warrant_synapse, timeout=10
172
+ )
173
+ logger.info(f"💊 Received {len(task_warrant_responses)} responses from miners for TaskWarrantProtocol requests💊")
174
+
175
+ upscaling_miners = []
176
+ compression_miners = []
177
+ unknown_task_miners = []
178
+
179
+ for i, response in enumerate(task_warrant_responses):
180
+ uid = random_uids[i]
181
+ axon = axons[i]
182
+
183
+ if response.warrant_task == TaskType.UPSCALING:
184
+ upscaling_miners.append((axon, uid))
185
+ elif response.warrant_task == TaskType.COMPRESSION:
186
+ compression_miners.append((axon, uid))
187
+ else:
188
+ unknown_task_miners.append(uid)
189
+
190
+ if unknown_task_miners:
191
+ logger.info(f"🔍 Checking database for {len(unknown_task_miners)} unknown task warrant miners")
192
+ db_task_types = self.miner_manager.get_miner_processing_task_types(unknown_task_miners)
193
+
194
+ resolved_from_db = []
195
+ defaulted_to_upscaling = []
196
+
197
+ for i, uid in enumerate(unknown_task_miners):
198
+ axon = axons[random_uids.index(uid)]
199
+
200
+ if uid in db_task_types:
201
+ db_task_type = db_task_types[uid]
202
+ if db_task_type == "upscaling":
203
+ upscaling_miners.append((axon, uid))
204
+ resolved_from_db.append(f"{uid}(upscaling)")
205
+ elif db_task_type == "compression":
206
+ compression_miners.append((axon, uid))
207
+ resolved_from_db.append(f"{uid}(compression)")
208
+ else:
209
+ upscaling_miners.append((axon, uid))
210
+ defaulted_to_upscaling.append(f"{uid}(unknown_db_value:{db_task_type})")
211
+ else:
212
+ upscaling_miners.append((axon, uid))
213
+ defaulted_to_upscaling.append(f"{uid}(no_db_record)")
214
+
215
+ upscaling_uids = [uid for _, uid in upscaling_miners]
216
+ compression_uids = [uid for _, uid in compression_miners]
217
+ all_uids = upscaling_uids + compression_uids
218
+
219
+ await self.refresh_miner_manager(all_uids)
220
+
221
+ logger.info(f"🛜 Grouped miners: {len(upscaling_miners)} upscaling, {len(compression_miners)} compression 🛜")
222
+ if upscaling_uids:
223
+ logger.info(f"📈 Upscaling UIDs: {upscaling_uids}")
224
+ if compression_uids:
225
+ logger.info(f"📉 Compression UIDs: {compression_uids}")
226
+ if unknown_task_miners:
227
+ logger.info(f"❓ Unknown task UIDs processed: {unknown_task_miners}")
228
+
229
+ if upscaling_miners:
230
+ logger.info(f"Sending LengthCheckProtocol requests to {len(upscaling_miners)} upscaling miners")
231
+
232
+ upscaling_start_time = time.time()
233
+ upscaling_axons = [miner[0] for miner in upscaling_miners]
234
+ length_check_synapse = LengthCheckProtocol(version=version)
235
+
236
+ length_check_responses = await self.dendrite.forward(
237
+ axons=upscaling_axons, synapse=length_check_synapse, timeout=10
238
+ )
239
+ logger.info(f"💊 Received {len(length_check_responses)} responses from upscaling miners for LengthCheckProtocol requests💊")
240
+
241
+ upscaling_content_lengths = []
242
+ for response in length_check_responses:
243
+ avail_max_len = response.max_content_length.value
244
+ if avail_max_len == 10:
245
+ upscaling_content_lengths.append(avail_max_len)
246
+ else:
247
+ upscaling_content_lengths.append(5)
248
+
249
+ logger.info(f"Upscaling content lengths: {upscaling_content_lengths}")
250
+
251
+ upscaling_miners_with_lengths = []
252
+ for i, (axon, uid) in enumerate(upscaling_miners):
253
+ content_length = upscaling_content_lengths[i] if i < len(upscaling_content_lengths) else 5
254
+ upscaling_miners_with_lengths.append((axon, uid, content_length))
255
+
256
+ await self.process_upscaling_miners(upscaling_miners_with_lengths, version)
257
+
258
+ upscaling_processed_time = time.time() - upscaling_start_time
259
+
260
+ logger.info(f"Upscaling tasks processed in {upscaling_processed_time:.2f} seconds")
261
+
262
+ await asyncio.sleep(2)
263
+
264
+ if compression_miners:
265
+ logger.info(f"Processing {len(compression_miners)} compression miners")
266
+
267
+ compression_start_time = time.time()
268
+ await self.process_compression_miners(compression_miners, version)
269
+
270
+ compression_processed_time = time.time() - compression_start_time
271
+
272
+ logger.info(f"Compression tasks processed in {compression_processed_time:.2f} seconds")
273
+
274
+ await asyncio.sleep(2)
275
+
276
+ epoch_processed_time = time.time() - epoch_start_time
277
+ logger.info(f"Completed one epoch within {epoch_processed_time:.2f} seconds")
278
+
279
+ if epoch_processed_time < 60: # if epoch completed within 60 seconds in case of no miners requiring synth checking, sleep for 10 minutes
280
+ await asyncio.sleep(60 * 10)
281
+ else:
282
+ await asyncio.sleep(2)
283
+
284
+ # --------------------------------------------------------------------------- #
285
+ # Helper – turn the raw list of (axon, uid) into a dict {uid: (axon, uid)}
286
+ # --------------------------------------------------------------------------- #
287
+ def _miner_lookup(self, miners: List[Tuple[Any, int]]) -> dict[int, Tuple[Any, int]]:
288
+ """{uid: (axon, uid)} – fast lookup while preserving the original tuple."""
289
+ return {uid: (axon, uid) for axon, uid in miners}
290
+
291
+
292
+ async def create_miner_batches(
293
+ self,
294
+ miners: List[Tuple[Any, int]],
295
+ batch_size: int,
296
+ task_type: str = "upscaling",
297
+ ) -> List[List[Tuple[Any, int]]]:
298
+ """
299
+ Build batches of miners for *task_type* (upscaling / compression).
300
+
301
+ Rules
302
+ -----
303
+ 1. Miners with **fewest** performance-history rows go first.
304
+ 2. Miners whose **most recent entry** is older than 2 h are eligible, subject to 70% probability of selection in batching.
305
+ 3. Miners that already have >= CONFIG.score.max_performance_records rows
306
+ **and** whose latest entry is < 2 h are **skipped**.
307
+ """
308
+ session = self.miner_manager.session
309
+ hours_threshold = datetime.utcnow() - timedelta(hours=CONFIG.score.synthetics_hours_threshold)
310
+ max_records = CONFIG.score.max_performance_records
311
+
312
+ info_new_uids = []
313
+
314
+ # ------------------------------------------------------------------- #
315
+ # 1. One-shot aggregated stats per UID (count + latest timestamp)
316
+ # ------------------------------------------------------------------- #
317
+ uid_stats_subq = (
318
+ session.query(
319
+ MinerPerformanceHistory.uid,
320
+ func.count(MinerPerformanceHistory.id).label("record_count"),
321
+ func.max(MinerPerformanceHistory.timestamp).label("latest_timestamp"),
322
+ )
323
+ .filter(
324
+ MinerPerformanceHistory.processed_task_type == task_type,
325
+ MinerPerformanceHistory.uid.in_([uid for _axon, uid in miners]),
326
+ )
327
+ .group_by(MinerPerformanceHistory.uid)
328
+ .subquery()
329
+ )
330
+
331
+ uid_stats = session.query(
332
+ uid_stats_subq.c.uid,
333
+ uid_stats_subq.c.record_count,
334
+ uid_stats_subq.c.latest_timestamp,
335
+ ).all()
336
+
337
+ # uid → (record_count, latest_timestamp)
338
+ uid_info: dict[int, Tuple[int, datetime | None]] = {
339
+ row.uid: (row.record_count or 0, row.latest_timestamp) for row in uid_stats
340
+ }
341
+
342
+ # ------------------------------------------------------------------- #
343
+ # 2. Miners that have *zero* rows are the highest priority
344
+ # ------------------------------------------------------------------- #
345
+ all_uids = {uid for _axon, uid in miners}
346
+ for uid in all_uids - uid_info.keys():
347
+ uid_info[uid] = (0, None)
348
+
349
+ # ------------------------------------------------------------------- #
350
+ # 3. Build a fast lookup {uid: original_tuple}
351
+ # ------------------------------------------------------------------- #
352
+ miner_by_uid = self._miner_lookup(miners)
353
+
354
+ # ------------------------------------------------------------------- #
355
+ # 4. Filter + priority key
356
+ # ------------------------------------------------------------------- #
357
+ eligible: List[Tuple[Tuple[int, datetime], Tuple[Any, int]]] = []
358
+
359
+ for uid in all_uids:
360
+ miner_tuple = miner_by_uid[uid]
361
+ rec_cnt, latest_ts = uid_info[uid]
362
+
363
+ # ---- Rule 3: skip capped miners with recent performance record ----
364
+ if rec_cnt >= max_records and latest_ts and latest_ts >= hours_threshold:
365
+ logger.info(
366
+ f"Skipping miner uid={uid}: {rec_cnt} records, last update {latest_ts}"
367
+ )
368
+ continue
369
+
370
+ # ---- Rule 2: include if never seen, below capacity or stale ----
371
+ if rec_cnt < max_records or not latest_ts \
372
+ or (latest_ts < hours_threshold and random.random() < CONFIG.score.synthetics_select_probability):
373
+ priority = (
374
+ rec_cnt, # fewer records first
375
+ latest_ts if latest_ts else datetime.min, # oldest first
376
+ )
377
+ if rec_cnt == 0:
378
+ info_new_uids.append(uid)
379
+ eligible.append((priority, miner_tuple))
380
+
381
+ # ------------------------------------------------------------------- #
382
+ # 5. Sort & batch
383
+ # ------------------------------------------------------------------- #
384
+ eligible.sort(key=lambda x: x[0]) # by priority tuple
385
+ sorted_miners = [miner for _prio, miner in eligible]
386
+
387
+ batches = [
388
+ sorted_miners[i : i + batch_size]
389
+ for i in range(0, len(sorted_miners), batch_size)
390
+ ]
391
+
392
+ logger.info(
393
+ f"Created {len(batches)} {task_type} batches (size≤{batch_size}). "
394
+ f"Selected {len(sorted_miners)}/{len(miners)} miners."
395
+ f" New UIDs with no history: {info_new_uids}"
396
+ )
397
+ if eligible:
398
+ top_uid = eligible[0][1][1] # (axon, uid) → uid
399
+ logger.info(
400
+ f"Top-priority miner uid={top_uid} – records={uid_info[top_uid][0]}"
401
+ )
402
+
403
+ return batches
404
+
405
+ async def call_miner(self, axon, synapse, uid, timeout=60):
406
+ start = time.perf_counter()
407
+
408
+ try:
409
+ raw = await self.dendrite.forward(
410
+ axons=[axon],
411
+ synapse=synapse,
412
+ timeout=timeout,
413
+ )
414
+ duration = (time.perf_counter() - start) * 1000 # ms
415
+
416
+ try:
417
+ result = raw[0]
418
+ except Exception as e:
419
+ logger.error(
420
+ f"UID {uid} → invalid response structure after {duration:.2f} ms | {e}",
421
+ exc_info=True
422
+ )
423
+ return {
424
+ "uid": uid,
425
+ "result": None,
426
+ "error": e,
427
+ "latency_ms": duration,
428
+ }
429
+
430
+ logger.info(f"UID {uid} → success | {duration:.2f} ms")
431
+
432
+ return {
433
+ "uid": uid,
434
+ "result": result,
435
+ "error": None,
436
+ "latency_ms": duration,
437
+ }
438
+
439
+ except Exception as e:
440
+ duration = (time.perf_counter() - start) * 1000 # ms
441
+
442
+ logger.error(
443
+ f"UID {uid} → failure after {duration:.2f} ms | {type(e).__name__}: {e}",
444
+ exc_info=True
445
+ )
446
+
447
+ return {
448
+ "uid": uid,
449
+ "result": None, # EXACT same shape as original gather usage
450
+ "error": e,
451
+ "latency_ms": duration,
452
+ }
453
+
454
+ async def process_upscaling_miners(self, upscaling_miners_with_lengths, version):
455
+ """Process upscaling miners in batches similar to the original implementation."""
456
+ batch_size = CONFIG.bandwidth.requests_per_synthetic_interval
457
+
458
+ upscaling_miners = [(axon, uid) for axon, uid, _ in upscaling_miners_with_lengths]
459
+ upscaling_content_lengths = {uid: length for _, uid, length in upscaling_miners_with_lengths}
460
+
461
+ miner_batches = await self.create_miner_batches(upscaling_miners, batch_size, task_type="upscaling")
462
+
463
+ logger.info(f"Created {len(miner_batches)} upscaling batches of size {batch_size}")
464
+
465
+ for batch_idx, batch in enumerate(miner_batches):
466
+ batch_start_time = time.time()
467
+ logger.info(f"🧩 Processing upscaling batch {batch_idx + 1}/{len(miner_batches)} 🧩")
468
+
469
+ uids = []
470
+ axons = []
471
+ content_lengths = []
472
+ for miner in batch:
473
+ content_lengths.append(upscaling_content_lengths[miner[1]])
474
+ uids.append(miner[1])
475
+ axons.append(miner[0])
476
+
477
+ round_id = str(uuid.uuid4())
478
+ payload_urls, video_ids, uploaded_object_names, synapses, task_types = await self.challenge_synthesizer.build_synthetic_protocol(content_lengths, version, round_id)
479
+ logger.info(f"Built compression challenge protocol: payload URLs: {payload_urls}\nvideo IDs: {video_ids}")
480
+ logger.debug(f"Built upscaling challenge protocol")
481
+
482
+ timestamp = datetime.now(timezone.utc).isoformat()
483
+
484
+ forward_tasks = [
485
+ self.call_miner(axon, synapse, uid, timeout=60)
486
+ for uid, axon, synapse in zip(uids, axons, synapses)
487
+ ]
488
+ raw_responses = await asyncio.gather(*forward_tasks)
489
+ responses = [response['result'] for response in raw_responses]
490
+
491
+ logger.info(f"🎲 Received {len(responses)} upscaling responses from miners 🎲")
492
+
493
+ reference_video_paths = []
494
+ for video_id in video_ids:
495
+ reference_video_path = get_trim_video_path(video_id)
496
+ if not os.path.exists(reference_video_path):
497
+ logger.warning(f"⚠️ Reference video file missing for video_id {video_id}: {reference_video_path}")
498
+ reference_video_paths.append(reference_video_path)
499
+
500
+ asyncio.create_task(self.score_upscalings(uids, responses, payload_urls, reference_video_paths, timestamp, video_ids, uploaded_object_names, content_lengths, task_types, round_id))
501
+
502
+ batch_processed_time = time.time() - batch_start_time
503
+
504
+ sleep_time = random.uniform(SLEEP_TIME_LOW, SLEEP_TIME_HIGH) - batch_processed_time
505
+ logger.info(f"Completed upscaling batch within {batch_processed_time:.2f} seconds")
506
+ logger.info(f"Sleeping for 3-4 minutes before next upscaling batch")
507
+
508
+ await asyncio.sleep(sleep_time)
509
+
510
+ async def process_compression_miners(self, compression_miners, version):
511
+ """Process compression miners in batches similar to upscaling but with compression protocols."""
512
+ batch_size = CONFIG.bandwidth.requests_per_synthetic_interval
513
+
514
+ miner_batches = await self.create_miner_batches(compression_miners, batch_size, task_type="compression")
515
+
516
+ logger.info(f"Created {len(miner_batches)} compression batches of size {batch_size}")
517
+
518
+ for batch_idx, batch in enumerate(miner_batches):
519
+ batch_start_time = time.time()
520
+ logger.info(f"🧩 Processing compression batch {batch_idx + 1}/{len(miner_batches)} 🧩")
521
+
522
+ uids = []
523
+ axons = []
524
+ for miner in batch:
525
+ uids.append(miner[1])
526
+ axons.append(miner[0])
527
+
528
+ vmaf_threshold = random.choice(VMAF_QUALITY_THRESHOLDS)
529
+
530
+ round_id = str(uuid.uuid4())
531
+
532
+ num_miners = len(uids)
533
+
534
+ payload_urls, video_ids, uploaded_object_names, synapses = await self.challenge_synthesizer.build_compression_protocol(vmaf_threshold, num_miners, version, round_id)
535
+ logger.info(f"Built compression challenge protocol: payload URLs: {payload_urls}\nvideo IDs: {video_ids}")
536
+ logger.debug(f"Built compression challenge protocol")
537
+
538
+ timestamp = datetime.now(timezone.utc).isoformat()
539
+
540
+ logger.debug(f"Processing compression UIDs in batch: {uids}")
541
+
542
+ forward_tasks = [
543
+ self.call_miner(axon, synapse, uid, timeout=60)
544
+ for uid, axon, synapse in zip(uids, axons, synapses)
545
+ ]
546
+ raw_responses = await asyncio.gather(*forward_tasks)
547
+ responses = [response['result'] for response in raw_responses]
548
+
549
+ logger.info(f"🎲 Received {len(responses)} compression responses from miners 🎲")
550
+
551
+ reference_video_paths = []
552
+ for video_id in video_ids:
553
+ reference_video_path = get_trim_video_path(video_id)
554
+ if not os.path.exists(reference_video_path):
555
+ logger.warning(f"⚠️ Reference video file missing for video_id {video_id}: {reference_video_path}")
556
+ reference_video_paths.append(reference_video_path)
557
+
558
+ asyncio.create_task(self.score_compressions(uids, responses, payload_urls, reference_video_paths, timestamp, video_ids, uploaded_object_names, vmaf_threshold, round_id))
559
+
560
+ batch_processed_time = time.time() - batch_start_time
561
+ sleep_time = random.uniform(SLEEP_TIME_LOW, SLEEP_TIME_HIGH) - batch_processed_time
562
+
563
+ logger.info(f"Completed compression batch within {batch_processed_time:.2f} seconds")
564
+ logger.info(f"Sleeping for 3-4 minutes before next compression batch")
565
+
566
+ await asyncio.sleep(sleep_time)
567
+
568
+
569
+ async def start_organic_loop(self):
570
+ """Start organic processing loop for both upscaling and compression tasks asynchronously."""
571
+ try:
572
+ # Create tasks for both upscaling and compression processing
573
+ upscaling_task = asyncio.create_task(self._process_organic_upscaling_loop())
574
+ compression_task = asyncio.create_task(self._process_organic_compression_loop())
575
+
576
+ # Wait for both tasks to complete (they run indefinitely)
577
+ await asyncio.gather(upscaling_task, compression_task)
578
+ except Exception as e:
579
+ logger.error(f"Error during organic processing loop: {e}")
580
+
581
+ async def _process_organic_upscaling_loop(self):
582
+ """Process organic upscaling tasks in a loop."""
583
+ while True:
584
+ try:
585
+ await self.should_process_organic_upscaling()
586
+ await asyncio.sleep(5)
587
+ except Exception as e:
588
+ logger.error(f"Error during organic upscaling processing: {e}")
589
+ await asyncio.sleep(5)
590
+
591
+ async def _process_organic_compression_loop(self):
592
+ """Process organic compression tasks in a loop."""
593
+ while True:
594
+ try:
595
+ await self.should_process_organic_compression()
596
+ await asyncio.sleep(5)
597
+ except Exception as e:
598
+ logger.error(f"Error during organic compression processing: {e}")
599
+ await asyncio.sleep(5)
600
+
601
+ async def score_upscalings(
602
+ self,
603
+ uids: list[int],
604
+ responses: list[protocol.Synapse],
605
+ payload_urls: list[str],
606
+ reference_video_paths: list[str],
607
+ timestamp: str,
608
+ video_ids: list[str],
609
+ uploaded_object_names: list[str],
610
+ content_lengths: list[int],
611
+ task_types: list[str],
612
+ round_id: str
613
+ ):
614
+ distorted_urls = []
615
+ for uid, response in zip(uids, responses):
616
+ distorted_urls.append(response.miner_response.optimized_video_url)
617
+
618
+ logger.info(f"responses: {responses}")
619
+
620
+ score_response = await self.score_client.post(
621
+ "/score_upscaling_synthetics",
622
+ json = {
623
+ "uids": uids,
624
+ "payload_urls": payload_urls,
625
+ "distorted_urls": distorted_urls,
626
+ "reference_paths": reference_video_paths,
627
+ "video_ids": video_ids,
628
+ "uploaded_object_names": uploaded_object_names,
629
+ "content_lengths": content_lengths,
630
+ "task_types": task_types
631
+ },
632
+ timeout=240
633
+ )
634
+
635
+ response_data = score_response.json()
636
+
637
+ quality_scores = response_data.get("quality_scores", [])
638
+ length_scores = response_data.get("length_scores", [])
639
+ final_scores = response_data.get("final_scores", [])
640
+ vmaf_scores = response_data.get("vmaf_scores", [])
641
+ pieapp_scores = response_data.get("pieapp_scores", [])
642
+ reasons = response_data.get("reasons", [])
643
+
644
+ logger.info(f"Updating miner manager with {len(quality_scores)} miner scores after synthetic requests processing")
645
+
646
+ miner_hotkeys = [self.metagraph.hotkeys[uid] for uid in uids]
647
+
648
+ accumulate_scores, applied_multipliers = self.miner_manager.step_synthetics_upscaling(
649
+ round_id, uids, miner_hotkeys, vmaf_scores, pieapp_scores, quality_scores, length_scores, final_scores, content_lengths
650
+ )
651
+
652
+ max_length = max(
653
+ len(uids),
654
+ len(quality_scores),
655
+ len(length_scores),
656
+ len(final_scores),
657
+ len(vmaf_scores),
658
+ len(pieapp_scores),
659
+ len(reasons),
660
+ len(content_lengths),
661
+ len(applied_multipliers),
662
+ len(accumulate_scores),
663
+ )
664
+
665
+ vmaf_scores.extend([0.0] * (max_length - len(vmaf_scores)))
666
+ pieapp_scores.extend([0.0] * (max_length - len(pieapp_scores)))
667
+ quality_scores.extend([0.0] * (max_length - len(quality_scores)))
668
+ length_scores.extend([0.0] * (max_length - len(length_scores)))
669
+ final_scores.extend([0.0] * (max_length - len(final_scores)))
670
+ reasons.extend(["No reason provided"] * (max_length - len(reasons)))
671
+ content_lengths.extend([0.0] * (max_length - len(content_lengths)))
672
+ applied_multipliers.extend([0.0] * (max_length - len(applied_multipliers)))
673
+
674
+ logger.info(f"Synthetic scoring results for {len(uids)} miners")
675
+ logger.info(f"Uids: {uids}")
676
+
677
+ for uid, vmaf_score, pieapp_score, quality_score, length_score, final_score, reason, content_length, applied_multiplier in zip(
678
+ uids, vmaf_scores, pieapp_scores, quality_scores, length_scores, final_scores, reasons, content_lengths, applied_multipliers
679
+ ):
680
+ logger.info(
681
+ f"{uid} ** VMAF: {vmaf_score:.2f} ** PieAPP: {pieapp_score:.2f} ** Quality: {quality_score:.4f} "
682
+ f"** Length: {length_score:.4f} ** Content Length: {content_length} ** Applied_multiplier {applied_multiplier} ** Final: {final_score:.4f} || {reason}"
683
+ )
684
+
685
+ miner_data = {
686
+ "validator_uid": self.my_subnet_uid,
687
+ "validator_hotkey": self.wallet.hotkey.ss58_address,
688
+ "request_type": "Synthetic",
689
+ "processing_task_type": "upscaling",
690
+ "miner_uids": uids,
691
+ "miner_hotkeys": miner_hotkeys,
692
+ "vmaf_scores": vmaf_scores,
693
+ "pieapp_scores": pieapp_scores,
694
+ "quality_scores": quality_scores,
695
+ "length_scores": length_scores,
696
+ "final_scores": final_scores,
697
+ "accumulate_scores": accumulate_scores,
698
+ "applied_multipliers": applied_multipliers,
699
+ "status": reasons,
700
+ "task_urls": payload_urls,
701
+ "processed_urls": distorted_urls,
702
+ "timestamp": timestamp
703
+ }
704
+
705
+ success = send_upscaling_data_to_dashboard(miner_data)
706
+ if success:
707
+ logger.info("Data successfully sent to dashboard")
708
+ else:
709
+ logger.info("Failed to send data to dashboard")
710
+
711
+ async def score_compressions(
712
+ self,
713
+ uids: list[int],
714
+ responses: list[protocol.Synapse],
715
+ payload_urls: list[str],
716
+ reference_video_paths: list[str],
717
+ timestamp: str,
718
+ video_ids: list[str],
719
+ uploaded_object_names: list[str],
720
+ vmaf_threshold: float,
721
+ round_id: str
722
+ ):
723
+ distorted_urls = []
724
+ for uid, response in zip(uids, responses):
725
+ distorted_urls.append(response.miner_response.optimized_video_url)
726
+
727
+ score_response = await self.score_client.post(
728
+ "/score_compression_synthetics",
729
+ json = {
730
+ "uids": uids,
731
+ "distorted_urls": distorted_urls,
732
+ "reference_paths": reference_video_paths,
733
+ "video_ids": video_ids,
734
+ "uploaded_object_names": uploaded_object_names,
735
+ "vmaf_threshold": vmaf_threshold
736
+ },
737
+ timeout=240
738
+ )
739
+
740
+ response_data = score_response.json()
741
+
742
+ compression_rates = response_data.get("compression_rates", [])
743
+ final_scores = response_data.get("final_scores", [])
744
+ vmaf_scores = response_data.get("vmaf_scores", [])
745
+ reasons = response_data.get("reasons", [])
746
+
747
+ logger.info(f"Updating miner manager with {len(compression_rates)} compression miner scores after synthetic requests processing")
748
+
749
+ miner_hotkeys = [self.metagraph.hotkeys[uid] for uid in uids]
750
+
751
+ if not compression_rates:
752
+ compression_rates = [0.5] * len(uids)
753
+
754
+ accumulate_scores, applied_multipliers = self.miner_manager.step_synthetics_compression(
755
+ round_id, uids, miner_hotkeys, vmaf_scores,
756
+ final_scores, [10] * len(uids), vmaf_threshold, compression_rates
757
+ )
758
+
759
+ max_length = max(
760
+ len(uids),
761
+ len(final_scores),
762
+ len(vmaf_scores),
763
+ len(reasons),
764
+ len(applied_multipliers),
765
+ len(accumulate_scores),
766
+ )
767
+
768
+ vmaf_scores.extend([0.0] * (max_length - len(vmaf_scores)))
769
+ final_scores.extend([0.0] * (max_length - len(final_scores)))
770
+ reasons.extend(["No reason provided"] * (max_length - len(reasons)))
771
+ compression_rates.extend([0.5] * (max_length - len(compression_rates)))
772
+ applied_multipliers.extend([0.0] * (max_length - len(applied_multipliers)))
773
+
774
+ vmaf_thresholds = [vmaf_threshold] * len(uids)
775
+
776
+ logger.info(f"Synthetic compression scoring results for {len(uids)} miners")
777
+ logger.info(f"Uids: {uids}")
778
+
779
+ for uid, vmaf_score, final_score, reason, compression_rate, applied_multiplier in zip(
780
+ uids, vmaf_scores, final_scores, reasons, compression_rates, applied_multipliers
781
+ ):
782
+ logger.info(
783
+ f"{uid} ** VMAF: {vmaf_score:.2f} "
784
+ f"** VMAF Threshold: {vmaf_threshold} ** Compression Rate: {compression_rate:.4f} ** Final: {final_score:.4f} || {reason}"
785
+ )
786
+
787
+ miner_data = {
788
+ "validator_uid": self.my_subnet_uid,
789
+ "validator_hotkey": self.wallet.hotkey.ss58_address,
790
+ "request_type": "Synthetic",
791
+ "processing_task_type": "compression",
792
+ "miner_uids": uids,
793
+ "miner_hotkeys": miner_hotkeys,
794
+ "vmaf_scores": vmaf_scores,
795
+ "vmaf_thresholds": vmaf_thresholds,
796
+ "compression_rates": compression_rates,
797
+ "final_scores": final_scores,
798
+ "accumulate_scores": accumulate_scores,
799
+ "applied_multipliers": applied_multipliers,
800
+ "status": reasons,
801
+ "task_urls": payload_urls,
802
+ "processed_urls": distorted_urls,
803
+ "timestamp": timestamp
804
+ }
805
+
806
+ success = send_compression_data_to_dashboard(miner_data)
807
+ if success:
808
+ logger.info("Compression data successfully sent to dashboard")
809
+ else:
810
+ logger.info("Failed to send compression data to dashboard")
811
+
812
+ async def score_organics_upscaling(self, uids: list[int], responses: list[protocol.Synapse], reference_urls: list[str], task_types: list[str], timestamp: str):
813
+ """Score organic upscaling tasks."""
814
+ distorted_urls = [response.miner_response.optimized_video_url for response in responses]
815
+
816
+ combined = list(zip(uids, distorted_urls, reference_urls, task_types))
817
+ random.shuffle(combined)
818
+ uids, distorted_urls, reference_urls, task_types = map(list, zip(*combined))
819
+
820
+ num_pairs_to_validate = min(1, len(combined))
821
+ selected_indices = random.sample(range(len(combined)), num_pairs_to_validate)
822
+
823
+ selected_uids = [uids[i] for i in selected_indices]
824
+ selected_distorted_urls = [distorted_urls[i] for i in selected_indices]
825
+ selected_reference_urls = [reference_urls[i] for i in selected_indices]
826
+ selected_task_types = [task_types[i] for i in selected_indices]
827
+
828
+ logger.info(f"Randomly selected {len(selected_uids)} pairs out of {len(uids)} total pairs for validation")
829
+
830
+ score_response = await self.score_client.post(
831
+ "/score_organics_upscaling",
832
+ json={
833
+ "uids": selected_uids,
834
+ "distorted_urls": selected_distorted_urls,
835
+ "reference_urls": selected_reference_urls,
836
+ "task_types": selected_task_types
837
+ },
838
+ timeout=38
839
+ )
840
+
841
+ response_data = score_response.json()
842
+ scores = response_data.get("final_scores", [])
843
+ vmaf_scores = response_data.get("vmaf_scores", [])
844
+ pieapp_scores = response_data.get("pieapp_scores", [])
845
+ quality_scores = response_data.get("quality_scores", [])
846
+ length_scores = response_data.get("length_scores", [])
847
+ reasons = response_data.get("reasons", [])
848
+
849
+ max_length = max(len(uids), len(scores), len(vmaf_scores), len(pieapp_scores), len(reasons))
850
+ scores.extend([0.0] * (max_length - len(scores)))
851
+ vmaf_scores.extend([0.0] * (max_length - len(vmaf_scores)))
852
+ pieapp_scores.extend([0.0] * (max_length - len(pieapp_scores)))
853
+ quality_scores.extend([0.0] * (max_length - len(quality_scores)))
854
+ length_scores.extend([0.0] * (max_length - len(length_scores)))
855
+ reasons.extend(["no reason provided"] * (max_length - len(reasons)))
856
+
857
+ logger.info(f"organic upscaling scoring results for {len(selected_uids)} miners")
858
+ logger.info(f"uids: {selected_uids}")
859
+ for uid, vmaf_score, score, reason in zip(selected_uids, vmaf_scores, scores, reasons):
860
+ logger.info(f"{uid} ** {vmaf_score:.2f} ** {score:.4f} || {reason}")
861
+
862
+ round_id = f"organic_upscaling_{int(time.time())}"
863
+
864
+ logger.info(f"updating miner manager with {len(scores)} miner scores after organic upscaling requests processing…")
865
+ accumulate_scores, applied_multipliers = self.miner_manager.step_organic_upscaling(scores, selected_uids, round_id)
866
+
867
+ miner_hotkeys = [self.metagraph.hotkeys[uid] for uid in selected_uids]
868
+
869
+ miner_data = {
870
+ "validator_uid": self.my_subnet_uid,
871
+ "validator_hotkey": self.wallet.hotkey.ss58_address,
872
+ "request_type": "Organic",
873
+ "processing_task_type": "upscaling",
874
+ "miner_uids": selected_uids,
875
+ "miner_hotkeys": miner_hotkeys,
876
+ "vmaf_scores": vmaf_scores,
877
+ "pieapp_scores": pieapp_scores,
878
+ "quality_scores": quality_scores,
879
+ "length_scores": length_scores,
880
+ "final_scores": scores,
881
+ "accumulate_scores": accumulate_scores,
882
+ "applied_multipliers": applied_multipliers,
883
+ "status": reasons,
884
+ "task_urls": selected_reference_urls,
885
+ "processed_urls": selected_distorted_urls,
886
+ "timestamp": timestamp
887
+ }
888
+
889
+ success = send_upscaling_data_to_dashboard(miner_data)
890
+ if success:
891
+ logger.info("Data successfully sent to dashboard")
892
+ else:
893
+ logger.info("Failed to send data to dashboard")
894
+
895
+ async def score_organics_compression(self, uids: list[int], responses: list[protocol.Synapse], reference_urls: list[str], vmaf_thresholds: list[float], timestamp: str):
896
+ """Score organic compression tasks."""
897
+ distorted_urls = [response.miner_response.optimized_video_url for response in responses]
898
+
899
+ combined = list(zip(uids, distorted_urls, reference_urls, vmaf_thresholds))
900
+ random.shuffle(combined)
901
+ uids, distorted_urls, reference_urls, vmaf_thresholds = map(list, zip(*combined))
902
+
903
+ num_pairs_to_validate = min(5, len(combined))
904
+ selected_indices = random.sample(range(len(combined)), num_pairs_to_validate)
905
+
906
+ selected_uids = [uids[i] for i in selected_indices]
907
+ selected_distorted_urls = [distorted_urls[i] for i in selected_indices]
908
+ selected_reference_urls = [reference_urls[i] for i in selected_indices]
909
+ selected_vmaf_thresholds = [vmaf_thresholds[i] for i in selected_indices]
910
+
911
+ logger.info(f"Randomly selected {len(selected_uids)} pairs out of {len(uids)} total pairs for compression validation")
912
+
913
+ score_response = await self.score_client.post(
914
+ "/score_organics_compression",
915
+ json={
916
+ "uids": selected_uids,
917
+ "distorted_urls": selected_distorted_urls,
918
+ "reference_urls": selected_reference_urls,
919
+ "vmaf_thresholds": selected_vmaf_thresholds
920
+ },
921
+ timeout=115
922
+ )
923
+
924
+ response_data = score_response.json()
925
+ scores = response_data.get("final_scores", [])
926
+ vmaf_scores = response_data.get("vmaf_scores", [])
927
+ compression_rates = response_data.get("compression_rates", [])
928
+ reasons = response_data.get("reasons", [])
929
+
930
+ max_length = max(len(selected_uids), len(scores), len(vmaf_scores), len(compression_rates), len(reasons))
931
+ scores.extend([0.0] * (max_length - len(scores)))
932
+ vmaf_scores.extend([0.0] * (max_length - len(vmaf_scores)))
933
+ compression_rates.extend([0.5] * (max_length - len(compression_rates)))
934
+ reasons.extend(["no reason provided"] * (max_length - len(reasons)))
935
+
936
+ round_id = f"organic_compression_{int(time.time())}"
937
+
938
+ logger.info(f"updating miner manager with {len(scores)} miner scores after organic compression requests processing…")
939
+ accumulate_scores, applied_multipliers = self.miner_manager.step_organic_compression(scores, selected_uids, vmaf_scores, compression_rates, selected_vmaf_thresholds, round_id)
940
+
941
+ logger.info(f"organic compression scoring results for {len(selected_uids)} miners")
942
+ logger.info(f"uids: {selected_uids}")
943
+ for uid, vmaf_score, final_score, reason, compression_rate, applied_multiplier, vmaf_threshold in zip(
944
+ selected_uids, vmaf_scores, scores, reasons, compression_rates, applied_multipliers, selected_vmaf_thresholds
945
+ ):
946
+ logger.info(
947
+ f"{uid} ** VMAF: {vmaf_score:.2f} "
948
+ f"** VMAF Threshold: {vmaf_threshold} ** Compression Rate: {compression_rate:.4f} ** Final: {final_score:.4f} || {reason}"
949
+ )
950
+
951
+ miner_hotkeys = [self.metagraph.hotkeys[uid] for uid in selected_uids]
952
+
953
+ miner_data = {
954
+ "validator_uid": self.my_subnet_uid,
955
+ "validator_hotkey": self.wallet.hotkey.ss58_address,
956
+ "request_type": "Organic",
957
+ "processing_task_type": "compression",
958
+ "miner_uids": selected_uids,
959
+ "miner_hotkeys": miner_hotkeys,
960
+ "vmaf_scores": vmaf_scores,
961
+ "vmaf_thresholds": selected_vmaf_thresholds,
962
+ "compression_rates": compression_rates,
963
+ "final_scores": scores,
964
+ "accumulate_scores": accumulate_scores,
965
+ "applied_multipliers": applied_multipliers,
966
+ "status": reasons,
967
+ "task_urls": selected_reference_urls,
968
+ "processed_urls": selected_distorted_urls,
969
+ "timestamp": timestamp
970
+ }
971
+
972
+ success = send_compression_data_to_dashboard(miner_data)
973
+ if success:
974
+ logger.info("Compression data successfully sent to dashboard")
975
+ else:
976
+ logger.info("Failed to send compression data to dashboard")
977
+
978
+ def filter_miners(self):
979
+ min_stake = CONFIG.bandwidth.min_stake
980
+ stake_array = self.metagraph.S
981
+ miner_uids = [i for i, stake in enumerate(stake_array) if stake < min_stake]
982
+
983
+ return miner_uids
984
+
985
+ async def should_process_organic_upscaling(self):
986
+ """Check if organic upscaling tasks should be processed."""
987
+ num_organic_upscaling_chunks = get_organic_upscaling_queue_size(self.redis_conn)
988
+
989
+ if num_organic_upscaling_chunks > 0:
990
+ logger.info(f"🔷 | UPSCALING | The organic_upscaling_queue_size: {num_organic_upscaling_chunks}, processing organic upscaling requests. 🔷")
991
+ await self.process_organic_upscaling_chunks(num_organic_upscaling_chunks)
992
+ return True
993
+ else:
994
+ return False
995
+
996
+ async def should_process_organic_compression(self):
997
+ """Check if organic compression tasks should be processed."""
998
+ num_organic_compression_chunks = get_organic_compression_queue_size(self.redis_conn)
999
+
1000
+ if num_organic_compression_chunks > 0:
1001
+ logger.info(f"🔷 | COMPRESSION | The organic_compression_queue_size: {num_organic_compression_chunks}, processing organic compression requests. 🔷")
1002
+ await self.process_organic_compression_chunks(num_organic_compression_chunks)
1003
+ return True
1004
+ else:
1005
+ return False
1006
+
1007
+ async def process_organic_upscaling_chunks(self, num_organic_chunks):
1008
+ """Process organic upscaling chunks."""
1009
+ organic_start_time = time.time()
1010
+
1011
+ needed = min(CONFIG.bandwidth.requests_per_organic_interval, num_organic_chunks)
1012
+
1013
+ logger.info(f"☘️ | UPSCALING | Start processing organic upscaling query. need {needed} miners ☘️")
1014
+
1015
+ forward_uids = get_organic_forward_uids(self, needed, "upscaling", CONFIG.bandwidth.min_stake)
1016
+
1017
+ if len(forward_uids) < needed:
1018
+ logger.info(f"There are just {len(forward_uids)} miners available for organic upscaling, handling {len(forward_uids)} chunks")
1019
+ needed = len(forward_uids)
1020
+
1021
+ axon_list = [self.metagraph.axons[uid] for uid in forward_uids]
1022
+
1023
+ task_ids, original_urls, task_types, synapses = await self.challenge_synthesizer.build_organic_upscaling_protocol(needed)
1024
+
1025
+ if len(task_ids) != needed or len(synapses) != needed:
1026
+ logger.error(
1027
+ f"Mismatch in organic upscaling synapses after building organic protocol: {len(task_ids)} != {needed} or {len(synapses)} != {needed}"
1028
+ )
1029
+ return
1030
+
1031
+ logger.info("Updating task status to 'processing' for upscaling")
1032
+ for task_id, original_url in zip(task_ids, original_urls):
1033
+ await self.update_task_status(task_id, original_url, "processing")
1034
+
1035
+ timestamp = datetime.now(timezone.utc).isoformat()
1036
+
1037
+ logger.info("🌜 | UPSCALING | Performing forward operations asynchronously for upscaling 🌜")
1038
+ forward_tasks = [
1039
+ self.call_miner(axon, synapse, uid, timeout=100)
1040
+ for uid, axon, synapse in zip(forward_uids, axon_list, synapses)
1041
+ ]
1042
+ raw_responses = await asyncio.gather(*forward_tasks)
1043
+ responses = [response['result'] for response in raw_responses]
1044
+
1045
+ processed_urls = [response.miner_response.optimized_video_url for response in responses]
1046
+
1047
+ logger.info(f"Processing organic upscaling chunks with uids: {forward_uids.tolist()}")
1048
+ logger.info("Updating task status to 'completed' and pushing results for upscaling")
1049
+ for task_id, original_url, processed_url in zip(task_ids, original_urls, processed_urls):
1050
+ await self.update_task_status(task_id, original_url, "completed")
1051
+ await self.push_result(task_id, original_url, processed_url)
1052
+
1053
+ asyncio.create_task(self.score_organics_upscaling(forward_uids.tolist(), responses, original_urls, task_types, timestamp))
1054
+
1055
+ end_time = time.time()
1056
+ total_time = end_time - organic_start_time
1057
+ logger.info(f"🍏 Organic upscaling chunk processing complete in {total_time:.2f} seconds 🍏")
1058
+
1059
+ async def process_organic_compression_chunks(self, num_organic_chunks):
1060
+ """Process organic compression chunks."""
1061
+ organic_start_time = time.time()
1062
+
1063
+ needed = min(CONFIG.bandwidth.requests_per_organic_interval, num_organic_chunks)
1064
+
1065
+ logger.info(f"☘️ | COMPRESSION | Start processing organic compression query. need {needed} miners ☘️")
1066
+
1067
+ forward_uids = get_organic_forward_uids(self, needed, "compression", CONFIG.bandwidth.min_stake)
1068
+
1069
+ if len(forward_uids) < needed:
1070
+ logger.info(f"There are just {len(forward_uids)} miners available for organic compression, handling {len(forward_uids)} chunks")
1071
+ needed = len(forward_uids)
1072
+
1073
+ axon_list = [self.metagraph.axons[uid] for uid in forward_uids]
1074
+
1075
+ task_ids, original_urls, vmaf_thresholds, synapses = await self.challenge_synthesizer.build_organic_compression_protocol(needed)
1076
+
1077
+ if len(task_ids) != needed or len(synapses) != needed:
1078
+ logger.error(
1079
+ f"Mismatch in organic compression synapses after building organic protocol: {len(task_ids)} != {needed} or {len(synapses)} != {needed}"
1080
+ )
1081
+ return
1082
+
1083
+ logger.info(f"Processing organic compression chunks with uids: {forward_uids.tolist()}")
1084
+ logger.info("Updating task status to 'processing' for compression")
1085
+ for task_id, original_url in zip(task_ids, original_urls):
1086
+ await self.update_task_status(task_id, original_url, "processing")
1087
+
1088
+ timestamp = datetime.now(timezone.utc).isoformat()
1089
+
1090
+ logger.info("🌜 | COMPRESSION | Performing forward operations asynchronously for compression 🌜")
1091
+
1092
+ forward_tasks = [
1093
+ self.call_miner(axon, synapse, uid, timeout=120)
1094
+ for uid, axon, synapse in zip(forward_uids, axon_list, synapses)
1095
+ ]
1096
+ raw_responses = await asyncio.gather(*forward_tasks)
1097
+ responses = [response['result'] for response in raw_responses]
1098
+
1099
+ processed_urls = [response.miner_response.optimized_video_url for response in responses]
1100
+
1101
+ logger.info("Updating task status to 'completed' and pushing results for compression")
1102
+ for task_id, original_url, processed_url in zip(task_ids, original_urls, processed_urls):
1103
+ await self.update_task_status(task_id, original_url, "completed")
1104
+ await self.push_result(task_id, original_url, processed_url)
1105
+
1106
+ asyncio.create_task(self.score_organics_compression(forward_uids.tolist(), responses, original_urls, vmaf_thresholds, timestamp))
1107
+
1108
+ end_time = time.time()
1109
+ total_time = end_time - organic_start_time
1110
+ logger.info(f"🍏 Organic compression chunk processing complete in {total_time:.2f} seconds 🍏")
1111
+
1112
+
1113
+ async def update_task_status(self, task_id, original_url, status):
1114
+ status_update_endpoint = f"{self.organic_gateway_base_url}/admin/task/{task_id}/status"
1115
+ status_update_payload = {
1116
+ "status": status,
1117
+ "original_video_url": original_url
1118
+ }
1119
+ try:
1120
+ async with httpx.AsyncClient() as client:
1121
+ response = await client.post(status_update_endpoint, json=status_update_payload)
1122
+ response.raise_for_status()
1123
+ except httpx.HTTPStatusError as e:
1124
+ logger.error(f"Error updating status for task {task_id}: {e.response.status_code} - {e.response.text}")
1125
+ except httpx.RequestError as e:
1126
+ logger.error(f"Request error updating status for task {task_id}: {e}")
1127
+
1128
+ async def push_result(self, task_id, original_url, processed_url):
1129
+ result_payload = {
1130
+ "processed_video_url": processed_url,
1131
+ "original_video_url": original_url,
1132
+ "score": 0,
1133
+ "task_id": task_id,
1134
+ }
1135
+ try:
1136
+ async with httpx.AsyncClient() as client:
1137
+ response = await client.post(self.push_result_endpoint, json=result_payload)
1138
+ response.raise_for_status()
1139
+ logger.info(f"Successfully pushed result for task {task_id}")
1140
+ except httpx.RequestError as e:
1141
+ logger.error(f"Error pushing result for task {task_id}: {e}")
1142
+
1143
+ def set_weights(self):
1144
+ self.current_block = self.subtensor.get_current_block()
1145
+ self.last_update = self.metagraph.last_update[self.uid]
1146
+ uids, weights = self.miner_manager.weights
1147
+ (
1148
+ processed_weight_uids,
1149
+ processed_weights,
1150
+ ) = bt.utils.weight_utils.process_weights_for_netuid(
1151
+ uids = uids,
1152
+ weights=weights,
1153
+ netuid=self.config.netuid,
1154
+ subtensor=self.subtensor,
1155
+ metagraph=self.metagraph,
1156
+ )
1157
+ (
1158
+ uint_uids,
1159
+ uint_weights,
1160
+ ) = bt.utils.weight_utils.convert_weights_and_uids_for_emit(
1161
+ uids=processed_weight_uids, weights=processed_weights
1162
+ )
1163
+ if self.current_block > self.last_update + CONFIG.SUBNET_TEMPO:
1164
+ weight_info = list(zip(uint_uids, uint_weights))
1165
+ weight_info_df = pd.DataFrame(weight_info, columns=["uid", "weight"])
1166
+ logger.info(f"Weight info:\n{weight_info_df.to_markdown()}")
1167
+ logger.info("Trying to set weights.")
1168
+ try:
1169
+ future = self.set_weights_executor.submit(
1170
+ self.subtensor.set_weights,
1171
+ netuid=self.config.netuid,
1172
+ wallet=self.wallet,
1173
+ uids=uint_uids,
1174
+ weights=uint_weights,
1175
+ )
1176
+ success, msg = future.result(timeout=120)
1177
+ if not success:
1178
+ logger.error(f"😠 Failed to set weights: {msg}")
1179
+ else:
1180
+ logger.debug("😎 Set weights successfully ")
1181
+ except Exception as e:
1182
+ logger.error(f"Failed to set weights: {e}")
1183
+ traceback.print_exc()
1184
+
1185
+ else:
1186
+ logger.info(
1187
+ f"Not setting weights because current block {self.current_block} is not greater than last update {self.last_update} + tempo {CONFIG.SUBNET_TEMPO}"
1188
+ )
1189
+
1190
+
1191
+ class WeightSynthesizer:
1192
+ def __init__(self, validator: Validator):
1193
+ self.validator = validator
1194
+
1195
+ async def run(self):
1196
+ while True:
1197
+ try:
1198
+ logger.info("Running weight_manager...")
1199
+ self.validator.set_weights()
1200
+ except Exception as e:
1201
+ logger.error(f"Error in WeightSynthesizer: {e}", exc_info=True)
1202
+ await asyncio.sleep(1200)
1203
+
1204
+
1205
+ if __name__ == "__main__":
1206
+ validator = Validator()
1207
+ weight_synthesizer = WeightSynthesizer(validator)
1208
+ time.sleep(1300) # wait till the video scheduler is ready
1209
+
1210
+ set_scheduler_ready(validator.redis_conn, False)
1211
+ logger.info("Set scheduler readiness flag to False")
1212
+
1213
+ async def main():
1214
+ validator_synthetic_task = asyncio.create_task(validator.run_synthetic())
1215
+ validator_organic_task = asyncio.create_task(validator.run_organic())
1216
+ weight_setter = asyncio.create_task(weight_synthesizer.run())
1217
+
1218
+ await asyncio.gather(validator_synthetic_task, validator_organic_task, weight_setter)
1219
+
1220
+ try:
1221
+ asyncio.run(main())
1222
+ except KeyboardInterrupt:
1223
+ logger.info("Program terminated by user.")
1224
+ except Exception as e:
1225
+ logger.error(f"Unhandled exception: {e}", exc_info=True)
1226
+
1227
+
pretrained/AIM_Training_2branche_KAN-Head.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:70c243d7324c76df43df8ab6a44eb535ee9f4f3acb928e5dfe9deb2bb3b7b0ab
3
+ size 85484315
pretrained/AIM_Training_2branche_MLP-Head.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:68116e2d548b6bec6d80982ce3bbd7fb1136b20e2db728669b3b307fb3e7a094
3
+ size 81899582
pretrained/single_branch_pretrained/Authentic.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fe09b8a81d2527165188cefd63e3f699a8df16aa4b0b631525734756696f2e6c
3
+ size 22135322
pretrained/single_branch_pretrained/Synthetic.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e9e7122c4e079f81227ff692f4680be2fdd99431ba90cdcf4a0656cfe93adc63
3
+ size 22135322
run.sh ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ script="neurons/validator.py"
4
+ autoRunLoc=$(readlink -f "$0")
5
+ proc_name="video-validator"
6
+ args=()
7
+ version_location="./vidaio_subnet_core/__init__.py"
8
+ version="__version__"
9
+ old_args=$@
10
+ subnet=85
11
+ restart_video_scheduler=true
12
+
13
+ if ! command -v pm2 &> /dev/null
14
+ then
15
+ echo "pm2 could not be found. To install see: https://pm2.keymetrics.io/docs/usage/quick-start/"
16
+ exit 1
17
+ fi
18
+
19
+ version_less_than_or_equal() {
20
+ [ "$1" = "$(echo -e "$1\n$2" | sort -V | head -n1)" ]
21
+ }
22
+
23
+ version_less_than() {
24
+ [ "$1" = "$2" ] && return 1 || version_less_than_or_equal $1 $2
25
+ }
26
+
27
+ read_version_value() {
28
+ while IFS= read -r line; do
29
+ if [[ "$line" == *"$version"* ]]; then
30
+ local value=$(echo "$line" | awk -F '=' '{print $2}' | tr -d ' ')
31
+ strip_quotes "$value"
32
+ return 0
33
+ fi
34
+ done < "$version_location"
35
+ echo ""
36
+ }
37
+
38
+ check_package_installed() {
39
+ local package_name="$1"
40
+ if ! command -v "$package_name" &> /dev/null; then
41
+ echo "Error: '$package_name' is not installed."
42
+ echo "Installing '$package_name'..."
43
+ sudo apt-get install -y "$package_name"
44
+ fi
45
+ }
46
+
47
+ strip_quotes() {
48
+ local input="$1"
49
+ local stripped="${input#\"}"
50
+ stripped="${stripped%\"}"
51
+ echo "$stripped"
52
+ }
53
+
54
+ check_variable_value_on_github() {
55
+ local repo="vidaio-subnet/vidaio-subnet"
56
+ local branch="$1"
57
+ local file_path="vidaio_subnet_core/__init__.py"
58
+ local variable="$2"
59
+
60
+
61
+ local content
62
+ content=$(curl -s "https://api.github.com/repos/$repo/contents/$file_path?ref=$branch" | jq -r '.content' | base64 --decode)
63
+
64
+ if [[ $? -ne 0 || -z "$content" ]]; then
65
+ echo "Error: Could not retrieve file content from GitHub."
66
+ return 1
67
+ fi
68
+
69
+ local value
70
+ value=$(echo "$content" | grep "$variable" | awk -F '=' '{print $2}' | tr -d ' ')
71
+
72
+ # Replace the strip_quotes call with direct quote removal
73
+ echo "$value" | tr -d '"' | tr -d "'"
74
+ }
75
+
76
+ ensure_process() {
77
+ local name="$1"
78
+ local cmd="$2"
79
+ local restart_flag="$3" # "true" or "false"
80
+
81
+ if pm2 describe "$name" &>/dev/null; then
82
+ echo "Process '$name' already running."
83
+ if [[ "$restart_flag" == "true" ]]; then
84
+ echo "Reloading $name..."
85
+ pm2 reload "$name"
86
+ fi
87
+ else
88
+ echo "Starting $name..."
89
+ pm2 start bash --name "$name" -- -c "$cmd"
90
+ fi
91
+ }
92
+
93
+ ensure_config_process() {
94
+ local config="$1"
95
+ local name="$2"
96
+ local restart_flag="$3" # "true" or "false"
97
+
98
+ if pm2 describe "$name" &>/dev/null; then
99
+ echo "Process '$name' already running (from $config)."
100
+ if [[ "$restart_flag" == "true" ]]; then
101
+ echo "Reloading $name from config..."
102
+ pm2 startOrReload "$config"
103
+ fi
104
+ else
105
+ echo "Starting $name from config..."
106
+ pm2 startOrReload "$config"
107
+ fi
108
+ }
109
+
110
+ while [[ $# -gt 0 ]]; do
111
+ arg="$1"
112
+ if [[ "$arg" == -* ]]; then
113
+ if [[ $# -gt 1 && "$2" != -* ]]; then
114
+ if [[ "$arg" == "--script" ]]; then
115
+ script="$2"
116
+ shift 2
117
+ elif [[ "$arg" == "--subnet" ]]; then
118
+ subnet="$2"
119
+ args+=("'--netuid'")
120
+ args+=("'$2'")
121
+ shift 2
122
+ elif [[ "$arg" == "--netuid" ]]; then
123
+ subnet="$2"
124
+ args+=("'$arg'")
125
+ args+=("'$2'")
126
+ shift 2
127
+ else
128
+ args+=("'$arg'")
129
+ args+=("'$2'")
130
+ shift 2
131
+ fi
132
+ else
133
+ if [[ "$arg" == "--no-video-scheduler" ]]; then
134
+ restart_video_scheduler=false
135
+ shift
136
+ else
137
+ args+=("'$arg'")
138
+ shift
139
+ fi
140
+ fi
141
+ else
142
+ args+=("'$arg'")
143
+ shift
144
+ fi
145
+ done
146
+
147
+ branch=$(git branch --show-current)
148
+ echo "Watching branch: $branch"
149
+ echo "Reapplying git stash"
150
+ git stash pop
151
+ echo "PM2 process name: $proc_name"
152
+ if [[ -n "$subnet" ]]; then
153
+ echo "Subnet: $subnet"
154
+ fi
155
+ echo "Restart video scheduler: $restart_video_scheduler"
156
+
157
+ current_version=$(read_version_value)
158
+
159
+ if pm2 status | grep -q $proc_name; then
160
+ echo "The script is already running with pm2. Stopping and restarting..."
161
+ pm2 delete $proc_name
162
+ fi
163
+
164
+ echo "Running $script with the following PM2 config:"
165
+
166
+ # Ensure netuid is always included in args
167
+ netuid_found=false
168
+ for arg in "${args[@]}"; do
169
+ if [[ "$arg" == "'--netuid'" ]]; then
170
+ netuid_found=true
171
+ break
172
+ fi
173
+ done
174
+
175
+ if [[ "$netuid_found" == "false" ]]; then
176
+ args+=("'--netuid'")
177
+ args+=("'$subnet'")
178
+ fi
179
+
180
+ joined_args=$(printf "%s," "${args[@]}")
181
+ joined_args=${joined_args%,}
182
+
183
+ echo "module.exports = {
184
+ apps : [{
185
+ name : '$proc_name',
186
+ script : '$script',
187
+ interpreter: 'python3',
188
+ min_uptime: '5m',
189
+ max_restarts: '5',
190
+ args: [$joined_args],
191
+ cwd: '$(pwd)',
192
+ env: {
193
+ PYTHONPATH: '.'
194
+ }
195
+ }]
196
+ }" > app.config.js
197
+
198
+ cat app.config.js
199
+ ensure_config_process "app.config.js" "$proc_name" "true"
200
+
201
+
202
+ check_package_installed "jq"
203
+
204
+ # 🚀 START THE ADDITIONAL PM2 PROCESSES
205
+ ensure_process "scoring_endpoint" "bash -c 'PYTHONPATH=. python services/scoring/server.py'" "true"
206
+ ensure_process "video_scheduler_worker" "bash -c 'PYTHONPATH=. python services/video_scheduler/worker.py'" "$restart_video_scheduler"
207
+ ensure_process "video_scheduler_endpoint" "bash -c 'PYTHONPATH=. python services/video_scheduler/server.py'" "$restart_video_scheduler"
208
+ ensure_process "organic-gateway" "bash -c 'PYTHONPATH=. python services/organic_gateway/server.py'" "true"
209
+
210
+ # Auto-update loop
211
+ last_restart_time=$(date +%s)
212
+ restart_interval=$((30 * 3600)) # 30 hours in seconds
213
+
214
+ while true; do
215
+ current_time=$(date +%s)
216
+ time_since_last_restart=$((current_time - last_restart_time))
217
+
218
+ if [ -d "./.git" ]; then
219
+ latest_version=$(check_variable_value_on_github "$branch" "$version")
220
+
221
+ if version_less_than $current_version $latest_version; then
222
+ echo "Latest version: $latest_version"
223
+ echo "Current version: $current_version"
224
+
225
+ git stash
226
+ if git pull origin "$branch"; then
227
+ echo "New version detected. Updating..."
228
+ pip install -e .
229
+
230
+ current_version=$(read_version_value)
231
+ last_restart_time=$current_time
232
+
233
+ echo "Restarting script..."
234
+ exec ./$(basename "$0") $old_args
235
+ else
236
+ echo "** Will not update **"
237
+ echo "You have uncommitted changes. Please stash them using 'git stash'."
238
+ fi
239
+ else
240
+ echo "** No update needed **"
241
+ echo "$current_version is up-to-date with $latest_version."
242
+
243
+ if [ $time_since_last_restart -ge $restart_interval ]; then
244
+ echo "30 hours passed. Performing periodic PM2 restart..."
245
+ pm2 restart scoring_endpoint
246
+ if [[ "$restart_video_scheduler" == "true" ]]; then
247
+ pm2 restart video_scheduler_worker
248
+ pm2 restart video_scheduler_endpoint
249
+ fi
250
+ pm2 restart video-validator
251
+
252
+ last_restart_time=$current_time
253
+ echo "Periodic restart completed."
254
+ fi
255
+ fi
256
+ else
257
+ echo "The installation does not appear to be from Git. Please install from source at https://github.com/vidaio-subnet/vidaio-subnet."
258
+ fi
259
+
260
+ sleep 1800 # Sleep 30 minutes before checking again
261
+ done
scripts/capture_video_frames.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import os
3
+ import argparse
4
+ import numpy as np
5
+ from pathlib import Path
6
+
7
+ def capture_frames(video_path, output_dir, num_frames=3):
8
+ """
9
+ Captures the first num_frames frames from a video file and resizes them to 128x128 pixels.
10
+
11
+ Args:
12
+ video_path (str): Path to the video file
13
+ output_dir (str): Directory to save the captured frames
14
+ num_frames (int): Number of frames to capture from the start
15
+
16
+ Returns:
17
+ list: Paths to the saved frames
18
+ """
19
+ # Check if video exists
20
+ if not os.path.isfile(video_path):
21
+ raise FileNotFoundError(f"Video file not found: {video_path}")
22
+
23
+ # Create output directory if it doesn't exist
24
+ os.makedirs(output_dir, exist_ok=True)
25
+
26
+ # Get video filename without extension
27
+ video_name = Path(video_path).stem
28
+
29
+ # Open the video file
30
+ cap = cv2.VideoCapture(video_path)
31
+
32
+ if not cap.isOpened():
33
+ raise Exception(f"Error opening video file: {video_path}")
34
+
35
+ # Get video properties
36
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
37
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
38
+ fps = cap.get(cv2.CAP_PROP_FPS)
39
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
40
+
41
+ print(f"Video Info:")
42
+ print(f" Resolution: {width}x{height}")
43
+ print(f" FPS: {fps:.2f}")
44
+ print(f" Total Frames: {total_frames}")
45
+
46
+ # Adjust num_frames if video is shorter
47
+ if total_frames < num_frames:
48
+ print(f"Warning: Video has only {total_frames} frames, adjusting to capture {total_frames} frames.")
49
+ num_frames = total_frames
50
+
51
+ saved_frames = []
52
+ for i in range(num_frames):
53
+ ret, frame = cap.read()
54
+ if not ret:
55
+ print(f"Error reading frame {i}")
56
+ break
57
+
58
+ resized_frame = cv2.resize(frame, (10, 10), interpolation=cv2.INTER_AREA)
59
+
60
+ # Save resized frame
61
+ frame_path = os.path.join(output_dir, f"{video_name}_frame_{i:03d}.png")
62
+ cv2.imwrite(frame_path, resized_frame)
63
+ saved_frames.append(frame_path)
64
+ print(f"Saved frame {i+1}/{num_frames}: {frame_path}")
65
+
66
+ cap.release()
67
+ return saved_frames
68
+
69
+ def main():
70
+ parser = argparse.ArgumentParser(description="Capture and resize frames from a video to 128x128 pixels")
71
+ parser.add_argument("--video", required=True, help="Path to the video file")
72
+ parser.add_argument("--output", default="frames", help="Directory to save the frames")
73
+ parser.add_argument("--frames", type=int, default=3, help="Number of frames to capture from the start")
74
+ args = parser.parse_args()
75
+
76
+ try:
77
+ saved_frames = capture_frames(args.video, args.output, args.frames)
78
+ print(f"Successfully captured {len(saved_frames)} frames from the video")
79
+ except Exception as e:
80
+ print(f"Error: {e}")
81
+
82
+ if __name__ == "__main__":
83
+ main()
scripts/check_queues.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Quick script to check video scheduler queue sizes
4
+ """
5
+
6
+ import sys
7
+ import os
8
+
9
+ # Add the services directory to Python path
10
+ # sys.path.append(os.path.join(os.path.dirname(__file__), 'services', 'video_scheduler'))
11
+
12
+ from services.video_scheduler.redis_utils import (
13
+ get_redis_connection,
14
+ get_5s_queue_size,
15
+ get_10s_queue_size,
16
+ get_organic_upscaling_queue_size,
17
+ get_organic_compression_queue_size,
18
+ get_pexels_queue_size,
19
+ get_youtube_queue_size,
20
+ is_scheduler_ready
21
+ )
22
+
23
+ def main():
24
+ """Check and display all queue sizes"""
25
+ try:
26
+ redis_conn = get_redis_connection()
27
+
28
+ print("=" * 50)
29
+ print("📊 VIDEO SCHEDULER QUEUE STATUS")
30
+ print("=" * 50)
31
+
32
+ # Scheduler readiness status
33
+ ready = is_scheduler_ready(redis_conn)
34
+ status_icon = "🟢" if ready else "🔴"
35
+ print(f"{status_icon} Scheduler Ready: {'YES' if ready else 'NO'}")
36
+ print()
37
+
38
+ # Synthetic queues
39
+ print("🎬 SYNTHETIC QUEUES:")
40
+ print(f" 5s chunks: {get_5s_queue_size(redis_conn):>4}")
41
+ print(f" 10s chunks: {get_10s_queue_size(redis_conn):>4}")
42
+ print(f" Compressed chunks: {get_organic_compression_queue_size(redis_conn):>4}")
43
+ print(f" Upscaling chunks: {get_organic_upscaling_queue_size(redis_conn):>4}")
44
+ print()
45
+
46
+ # Source queues
47
+ print("🎥 SOURCE QUEUES:")
48
+ print(f" Pexels IDs: {get_pexels_queue_size(redis_conn):>4}")
49
+ print(f" YouTube IDs: {get_youtube_queue_size(redis_conn):>4}")
50
+ print()
51
+
52
+
53
+ # Total counts
54
+ total_synthetic = get_5s_queue_size(redis_conn) + get_10s_queue_size(redis_conn) + get_organic_compression_queue_size(redis_conn) + get_organic_upscaling_queue_size(redis_conn)
55
+ total_source = get_pexels_queue_size(redis_conn) + get_youtube_queue_size(redis_conn)
56
+
57
+ print("📈 TOTALS:")
58
+ print(f" Total Synthetic: {total_synthetic:>4}")
59
+ print(f" Total Source: {total_source:>4}")
60
+ print("=" * 50)
61
+
62
+ except Exception as e:
63
+ print(f"❌ Error checking queues: {str(e)}")
64
+ print("💡 Make sure Redis is running and accessible")
65
+ sys.exit(1)
66
+
67
+ if __name__ == "__main__":
68
+ main()
scripts/clip_video.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Video Clipping Script
4
+
5
+ This script clips a video file to exactly 19 seconds starting from the beginning.
6
+ Uses ffmpeg for efficient video processing.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ import subprocess
12
+ from pathlib import Path
13
+ from typing import Optional, Tuple
14
+ import argparse
15
+
16
+ def _validate_video_file(file_path: str) -> bool:
17
+ """
18
+ Validate that the input file exists and is a readable video file.
19
+
20
+ Args:
21
+ file_path: Path to the video file
22
+
23
+ Returns:
24
+ True if file is valid, False otherwise
25
+ """
26
+ if not os.path.exists(file_path):
27
+ print(f"Error: File '{file_path}' does not exist.")
28
+ return False
29
+
30
+ if not os.path.isfile(file_path):
31
+ print(f"Error: '{file_path}' is not a file.")
32
+ return False
33
+
34
+ if not os.access(file_path, os.R_OK):
35
+ print(f"Error: File '{file_path}' is not readable.")
36
+ return False
37
+
38
+ return True
39
+
40
+ def _get_video_duration(file_path: str) -> Optional[float]:
41
+ """
42
+ Get the duration of a video file using ffprobe.
43
+
44
+ Args:
45
+ file_path: Path to the video file
46
+
47
+ Returns:
48
+ Duration in seconds, or None if ffprobe fails
49
+ """
50
+ try:
51
+ cmd = [
52
+ 'ffprobe',
53
+ '-v', 'quiet',
54
+ '-show_entries', 'format=duration',
55
+ '-of', 'csv=p=0',
56
+ file_path
57
+ ]
58
+
59
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
60
+ duration = float(result.stdout.strip())
61
+ return duration
62
+ except (subprocess.CalledProcessError, ValueError, FileNotFoundError) as e:
63
+ print(f"Error getting video duration: {e}")
64
+ return None
65
+
66
+ def _generate_output_filename(input_path: str) -> str:
67
+ """
68
+ Generate output filename by adding '_19s' suffix before the extension.
69
+
70
+ Args:
71
+ input_path: Path to the input video file
72
+
73
+ Returns:
74
+ Output filename with '_19s' suffix
75
+ """
76
+ input_path_obj = Path(input_path)
77
+ stem = input_path_obj.stem
78
+ suffix = input_path_obj.suffix
79
+
80
+ # Create output filename
81
+ output_filename = f"{stem}_19s{suffix}"
82
+
83
+ # If output would overwrite input, add timestamp
84
+ if output_filename == input_path_obj.name:
85
+ from datetime import datetime
86
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
87
+ output_filename = f"{stem}_19s_{timestamp}{suffix}"
88
+
89
+ return output_filename
90
+
91
+ def _clip_video(input_path: str, output_path: str, duration: int = 19) -> bool:
92
+ """
93
+ Clip the video to the specified duration using ffmpeg.
94
+
95
+ Args:
96
+ input_path: Path to the input video file
97
+ output_path: Path for the output video file
98
+ duration: Duration to clip to in seconds (default: 19)
99
+
100
+ Returns:
101
+ True if clipping was successful, False otherwise
102
+ """
103
+ try:
104
+ cmd = [
105
+ 'ffmpeg',
106
+ '-i', input_path,
107
+ '-t', str(duration),
108
+ '-c', 'copy', # Copy streams without re-encoding for speed
109
+ '-avoid_negative_ts', 'make_zero',
110
+ '-y', # Overwrite output file if it exists
111
+ output_path
112
+ ]
113
+
114
+ print(f"Cliping video to {duration} seconds...")
115
+ print(f"Input: {input_path}")
116
+ print(f"Output: {output_path}")
117
+
118
+ # Run ffmpeg command
119
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
120
+
121
+ print("Video clipping completed successfully!")
122
+ return True
123
+
124
+ except subprocess.CalledProcessError as e:
125
+ print(f"Error during video clipping: {e}")
126
+ if e.stderr:
127
+ print(f"FFmpeg error: {e.stderr}")
128
+ return False
129
+ except Exception as e:
130
+ print(f"Unexpected error: {e}")
131
+ return False
132
+
133
+ def clip_video_to_19s(video_path: str) -> bool:
134
+ """
135
+ Main function to clip a video file to 19 seconds.
136
+
137
+ Args:
138
+ video_path: Path to the input video file
139
+
140
+ Returns:
141
+ True if clipping was successful, False otherwise
142
+ """
143
+ # Validate input file
144
+ if not _validate_video_file(video_path):
145
+ return False
146
+
147
+ # Get original video duration
148
+ original_duration = _get_video_duration(video_path)
149
+ if original_duration is None:
150
+ return False
151
+
152
+ print(f"Original video duration: {original_duration:.2f} seconds")
153
+
154
+ # Check if video is already shorter than 19 seconds
155
+ if original_duration <= 19:
156
+ print(f"Video is already {original_duration:.2f} seconds long (≤ 19s). No clipping needed.")
157
+ return True
158
+
159
+ # Generate output filename
160
+ output_filename = _generate_output_filename(video_path)
161
+ output_path = os.path.join(os.path.dirname(video_path), output_filename)
162
+
163
+ # Perform video clipping
164
+ success = _clip_video(video_path, output_path, 19)
165
+
166
+ if success:
167
+ # Verify output file exists and get its duration
168
+ if os.path.exists(output_path):
169
+ output_duration = _get_video_duration(output_path)
170
+ if output_duration:
171
+ print(f"Output video duration: {output_duration:.2f} seconds")
172
+ print(f"Output saved to: {output_path}")
173
+ else:
174
+ print("Warning: Could not verify output video duration")
175
+ else:
176
+ print("Warning: Output file was not created")
177
+
178
+ return success
179
+
180
+ def main():
181
+ """Main entry point for command line usage."""
182
+ parser = argparse.ArgumentParser(
183
+ description="Clip a video file to 19 seconds",
184
+ formatter_class=argparse.RawDescriptionHelpFormatter,
185
+ epilog="""
186
+ Examples:
187
+ python clip_video.py video.mp4
188
+ python clip_video.py /path/to/video.mov
189
+ python clip_video.py "video with spaces.avi"
190
+ """
191
+ )
192
+
193
+ parser.add_argument(
194
+ 'video_path',
195
+ help='Path to the input video file'
196
+ )
197
+ #python clip_video.py --video_path /workspace/vidaio-subnet/2025-03-18 12-27-33.mov
198
+ args = parser.parse_args()
199
+
200
+ # Perform video clipping
201
+ success = clip_video_to_19s(args.video_path)
202
+
203
+ # Exit with appropriate code
204
+ sys.exit(0 if success else 1)
205
+
206
+ if __name__ == "__main__":
207
+ main()
scripts/compress_miner_king1_0.sh ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ python neurons/miner.py \
4
+ --wallet.name king85 \
5
+ --wallet.hotkey bothot00 \
6
+ --subtensor.network finney \
7
+ --netuid 85 \
8
+ --axon.port 8091 \
9
+ --logging.debug
10
+
scripts/compress_miner_king85_1.sh ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ python neurons/miner.py \
4
+ --wallet.name king85 \
5
+ --wallet.hotkey bothot01 \
6
+ --subtensor.network finney \
7
+ --netuid 85 \
8
+ --axon.port 8092 \
9
+ --logging.debug
10
+
scripts/compress_miner_king85_2.sh ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ python neurons/miner.py \
4
+ --wallet.name king85 \
5
+ --wallet.hotkey bothot02 \
6
+ --subtensor.network finney \
7
+ --netuid 85 \
8
+ --axon.port 8093 \
9
+ --logging.debug
10
+
scripts/compress_miner_king85_3.sh ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ python neurons/miner.py \
4
+ --wallet.name king85 \
5
+ --wallet.hotkey bothot03 \
6
+ --subtensor.network finney \
7
+ --netuid 85 \
8
+ --axon.port 8094 \
9
+ --logging.debug
10
+
scripts/compress_server.sh ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ python services/custom_compress/server.py
scripts/get_video_lengths.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Video Length Scanner Script
4
+
5
+ This script scans the /videos directory and prints a list of all video files
6
+ along with their durations in a readable format.
7
+ """
8
+
9
+ import os
10
+ import subprocess
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import List, Tuple, Optional
14
+
15
+ def get_video_duration(file_path: str) -> Optional[float]:
16
+ """
17
+ Get the duration of a video file using ffprobe.
18
+
19
+ Args:
20
+ file_path: Path to the video file
21
+
22
+ Returns:
23
+ Duration in seconds, or None if ffprobe fails
24
+ """
25
+ try:
26
+ # Use ffprobe to get video duration
27
+ cmd = [
28
+ 'ffprobe',
29
+ '-v', 'quiet',
30
+ '-show_entries', 'format=duration',
31
+ '-of', 'csv=p=0',
32
+ file_path
33
+ ]
34
+
35
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
36
+ duration = float(result.stdout.strip())
37
+ return duration
38
+ except (subprocess.CalledProcessError, ValueError, FileNotFoundError):
39
+ return None
40
+
41
+ def format_duration(seconds: float) -> str:
42
+ """
43
+ Format duration in seconds to a human-readable string.
44
+
45
+ Args:
46
+ seconds: Duration in seconds
47
+
48
+ Returns:
49
+ Formatted duration string (e.g., "1:23.45")
50
+ """
51
+ if seconds < 0:
52
+ return "Invalid"
53
+
54
+ minutes = int(seconds // 60)
55
+ remaining_seconds = seconds % 60
56
+
57
+ if minutes == 0:
58
+ return f"{remaining_seconds:.2f}s"
59
+ else:
60
+ return f"{minutes}:{remaining_seconds:05.2f}"
61
+
62
+ def categorize_duration(seconds: float) -> str:
63
+ """
64
+ Categorize duration into chunks (e.g., 10s, 20s, 30s).
65
+ Videos within ±0.5 seconds of a whole number are grouped together.
66
+
67
+ Args:
68
+ seconds: Duration in seconds
69
+
70
+ Returns:
71
+ Category string (e.g., "10s", "20s", "1m", "2m")
72
+ """
73
+ if seconds < 0:
74
+ return "Invalid"
75
+
76
+ # Round to nearest second for categorization
77
+ rounded_seconds = round(seconds)
78
+
79
+ if rounded_seconds < 60:
80
+ return f"{rounded_seconds}s"
81
+ else:
82
+ minutes = rounded_seconds // 60
83
+ remaining_seconds = rounded_seconds % 60
84
+ if remaining_seconds == 0:
85
+ return f"{minutes}m"
86
+ else:
87
+ return f"{minutes}m{remaining_seconds}s"
88
+
89
+ def scan_videos_directory(videos_dir: str = "videos") -> List[Tuple[str, float]]:
90
+ """
91
+ Scan the videos directory and return a list of video files with their durations.
92
+
93
+ Args:
94
+ videos_dir: Path to the videos directory
95
+
96
+ Returns:
97
+ List of tuples containing (filename, duration_in_seconds)
98
+ """
99
+ video_files = []
100
+
101
+ if not os.path.exists(videos_dir):
102
+ print(f"Error: Directory '{videos_dir}' does not exist.")
103
+ return []
104
+
105
+ # Supported video extensions
106
+ video_extensions = {'.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm', '.m4v'}
107
+
108
+ for filename in os.listdir(videos_dir):
109
+ file_path = os.path.join(videos_dir, filename)
110
+
111
+ # Check if it's a file and has a video extension
112
+ if os.path.isfile(file_path) and Path(filename).suffix.lower() in video_extensions:
113
+ duration = get_video_duration(file_path)
114
+ if duration is not None:
115
+ video_files.append((filename, duration))
116
+ else:
117
+ print(f"Warning: Could not get duration for {filename}")
118
+
119
+ return sorted(video_files, key=lambda x: x[1]) # Sort by duration
120
+
121
+ def main():
122
+ """Main function to run the video length scanner."""
123
+ print("Video Length Scanner")
124
+ print("=" * 50)
125
+
126
+ # Check if ffprobe is available
127
+ try:
128
+ subprocess.run(['ffprobe', '-version'], capture_output=True, check=True)
129
+ except (subprocess.CalledProcessError, FileNotFoundError):
130
+ print("Error: ffprobe is not installed or not in PATH.")
131
+ print("Please install FFmpeg to use this script.")
132
+ print("On Ubuntu/Debian: sudo apt install ffmpeg")
133
+ print("On CentOS/RHEL: sudo yum install ffmpeg")
134
+ print("On macOS: brew install ffmpeg")
135
+ sys.exit(1)
136
+
137
+ # Scan for videos
138
+ print("Scanning videos directory for video files...")
139
+ video_files = scan_videos_directory()
140
+
141
+ if not video_files:
142
+ print("No video files found in /videos directory.")
143
+ return
144
+
145
+ # Print results
146
+ print(f"\nFound {len(video_files)} video files:\n")
147
+ print(f"{'Filename':<50} {'Duration':<15} {'Size':<10}")
148
+ print("-" * 75)
149
+
150
+ total_duration = 0
151
+
152
+ for filename, duration in video_files:
153
+ # Get file size
154
+ file_path = os.path.join("videos", filename)
155
+ file_size = os.path.getsize(file_path)
156
+ size_mb = file_size / (1024 * 1024)
157
+
158
+ formatted_duration = format_duration(duration)
159
+ print(f"{filename:<50} {formatted_duration:<15} {size_mb:.1f}MB")
160
+ total_duration += duration
161
+
162
+ print("-" * 75)
163
+ print(f"{'Total':<50} {format_duration(total_duration):<15} {len(video_files)} files")
164
+
165
+ # Summary statistics
166
+ if video_files:
167
+ avg_duration = total_duration / len(video_files)
168
+ min_duration = min(video_files, key=lambda x: x[1])[1]
169
+ max_duration = max(video_files, key=lambda x: x[1])[1]
170
+
171
+ print(f"\nSummary Statistics:")
172
+ print(f"Average duration: {format_duration(avg_duration)}")
173
+ print(f"Shortest video: {format_duration(min_duration)}")
174
+ print(f"Longest video: {format_duration(max_duration)}")
175
+
176
+ # Duration category summary
177
+ print(f"\nDuration Categories:")
178
+ print("=" * 40)
179
+
180
+ # Group videos by duration category
181
+ duration_categories = {}
182
+ for filename, duration in video_files:
183
+ category = categorize_duration(duration)
184
+ if category not in duration_categories:
185
+ duration_categories[category] = []
186
+ duration_categories[category].append((filename, duration))
187
+
188
+ # Sort categories by duration (convert to seconds for sorting)
189
+ def category_sort_key(cat):
190
+ if 'm' in cat:
191
+ if 's' in cat:
192
+ # Format like "1m30s"
193
+ parts = cat.replace('m', ' ').replace('s', '').split()
194
+ return int(parts[0]) * 60 + int(parts[1])
195
+ else:
196
+ # Format like "2m"
197
+ return int(cat.replace('m', '')) * 60
198
+ else:
199
+ # Format like "30s"
200
+ return int(cat.replace('s', ''))
201
+
202
+ sorted_categories = sorted(duration_categories.items(), key=lambda x: category_sort_key(x[0]))
203
+
204
+ for category, videos in sorted_categories:
205
+ total_category_duration = sum(duration for _, duration in videos)
206
+ print(f"{category:<10} | {len(videos):>3} videos | Total: {format_duration(total_category_duration):<10} | Avg: {format_duration(total_category_duration/len(videos))}")
207
+
208
+ print("=" * 40)
209
+ print(f"Total categories: {len(duration_categories)}")
210
+
211
+ if __name__ == "__main__":
212
+ main()
scripts/video_info_cap.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import json
3
+ import sys
4
+
5
+ def get_video_info(file_path):
6
+ """
7
+ Get detailed video information using FFprobe.
8
+ :param file_path: Path to the video file
9
+ """
10
+ try:
11
+ # Run FFprobe to get video details in JSON format
12
+ command = [
13
+ "ffprobe",
14
+ "-v", "error", # Suppress unnecessary logs
15
+ "-show_format", # Show container format
16
+ "-show_streams", # Show codec and stream details
17
+ "-count_frames", # Count the number of frames
18
+ "-print_format", "json", # Output as JSON
19
+ file_path
20
+ ]
21
+
22
+ result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
23
+
24
+ # Parse the JSON output
25
+ video_info = json.loads(result.stdout)
26
+
27
+ # Print details
28
+ print("\n=== Video File Information ===")
29
+ print(f"File Path: {file_path}")
30
+ print("=" * 40)
31
+
32
+ # Format information
33
+ if "format" in video_info:
34
+ format_info = video_info["format"]
35
+ print(f"File Format: {format_info.get('format_name', 'N/A')}")
36
+ print(f"File Size: {int(format_info.get('size', 0)) / (1024 ** 2):.2f} MB")
37
+ print(f"Duration: {float(format_info.get('duration', 0)):.2f} seconds")
38
+ print(f"Bitrate: {int(format_info.get('bit_rate', 0)) / 1000:.2f} kbps")
39
+ print(f"Compressed: {'Yes' if 'bit_rate' in format_info else 'No'}")
40
+
41
+ print("\n=== Stream Information ===")
42
+ for stream in video_info.get("streams", []):
43
+ codec_type = stream.get("codec_type", "unknown").capitalize()
44
+ print(f"\nStream Type: {codec_type}")
45
+ print(f"Codec: {stream.get('codec_name', 'N/A')}")
46
+ print(f"Codec Long Name: {stream.get('codec_long_name', 'N/A')}")
47
+ if codec_type == "Video":
48
+ print(f"Resolution: {stream.get('width', 'N/A')}x{stream.get('height', 'N/A')}")
49
+ print(f"Frame Rate: {eval(stream.get('r_frame_rate', '0')):.2f} fps")
50
+ print(f"Number of Frames: {stream.get('nb_frames', 'N/A')}")
51
+ print(f"Pixel Format: {stream.get('pix_fmt', 'N/A')}")
52
+ elif codec_type == "Audio":
53
+ print(f"Sample Rate: {stream.get('sample_rate', 'N/A')} Hz")
54
+ print(f"Channels: {stream.get('channels', 'N/A')}")
55
+ print(f"Channel Layout: {stream.get('channel_layout', 'N/A')}")
56
+ print(f"Bitrate: {int(stream.get('bit_rate', 0)) / 1000:.2f} kbps")
57
+
58
+ print("\nDetailed Info Extraction Complete.")
59
+
60
+ except Exception as e:
61
+ print(f"Error: {e}")
62
+ sys.exit(1)
63
+
64
+ # Example usage
65
+ if __name__ == "__main__":
66
+ # Input video file path
67
+ video_path = input("Enter the path to the video file: ").strip()
68
+ get_video_info(video_path)
services/compress/__init__.py ADDED
File without changes
services/compress/encoder.py ADDED
@@ -0,0 +1,893 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import time
4
+ import torch
5
+ import pickle
6
+ import traceback
7
+ import subprocess
8
+ from sklearn.exceptions import NotFittedError
9
+ from sklearn.preprocessing import StandardScaler
10
+ from sklearn.utils.validation import check_is_fitted
11
+ from utils.processing_utils import (
12
+ encode_scene_with_size_check,
13
+ classify_scene_from_path
14
+ )
15
+ from utils.encode_video import encode_video
16
+ from utils.classify_scene import load_scene_classifier_model, CombinedModel
17
+
18
+ def get_cq_from_lookup_table(scene_type, config, target_vmaf=None, target_quality_level=None):
19
+ """
20
+ Look up the recommended CQ (Constant Quality) value for a scene type and target quality.
21
+
22
+ CQ values control encoding quality:
23
+ - Lower CQ = Higher quality, bigger file size (CQ 15-20)
24
+ - Higher CQ = Lower quality, smaller file size (CQ 30-35)
25
+
26
+ The lookup table now supports two selection modes:
27
+ - By explicit quality level via config['video_processing']['basic_cq_lookup_by_quality']
28
+ - By VMAF-derived tiers via config['video_processing']['basic_cq_lookup_tiered']
29
+ - High quality tier (VMAF 95+): Lower CQ values for better quality
30
+ - Medium quality tier (VMAF ~90): Balanced CQ values
31
+ - Low quality tier (VMAF ~85): Higher CQ values for smaller files
32
+
33
+ Args:
34
+ scene_type (str): Scene classification from AI model
35
+ config (dict): Configuration containing CQ lookup tables
36
+ target_vmaf (float, optional): Target VMAF score for quality tier (fallback)
37
+ target_quality_level (str, optional): 'High'|'Medium'|'Low' to select from by-quality table
38
+
39
+ Returns:
40
+ int: Recommended CQ value for encoding this scene type at target quality
41
+ """
42
+ vp = config.get('video_processing', {})
43
+
44
+ if target_quality_level:
45
+ norm = {'high': 'High', 'medium': 'Medium', 'low': 'Low'}
46
+ q_key = norm.get(str(target_quality_level).lower(), target_quality_level)
47
+ by_quality = vp.get('basic_cq_lookup_by_quality', {})
48
+ if isinstance(by_quality, dict):
49
+ table = by_quality.get(q_key) or by_quality.get(q_key.lower()) or {}
50
+ if isinstance(table, dict):
51
+ return table.get(scene_type, table.get('default', 25))
52
+
53
+ # Determine quality tier based on target VMAF (fallback)
54
+ if target_vmaf is None:
55
+ quality_tier = 'medium' # Default to medium if no target specified
56
+ elif target_vmaf >= 93:
57
+ quality_tier = 'high'
58
+ elif target_vmaf >= 88:
59
+ quality_tier = 'medium'
60
+ else:
61
+ quality_tier = 'low'
62
+
63
+ default_cq_map = {
64
+ # High quality tier (lower CQ = higher quality)
65
+ 'high': {
66
+ 'animation': 25, # Cartoons compress well naturally
67
+ 'low-action': 23, # Text/faces need moderate CQ for clarity
68
+ 'medium-action': 21, # Balanced CQ for general content
69
+ 'high-action': 19, # Gaming/sports need lower CQ for quality
70
+ 'default': 22 # Safe middle-ground when unsure
71
+ },
72
+ # Medium quality tier (balanced CQ)
73
+ 'medium': {
74
+ 'animation': 28, # Cartoons compress well, can use higher CQ
75
+ 'low-action': 26, # Text/faces need moderate CQ for clarity
76
+ 'medium-action': 24, # Balanced CQ for general content
77
+ 'high-action': 22, # Gaming/sports need lower CQ for quality
78
+ 'default': 25 # Safe middle-ground when unsure
79
+ },
80
+ # Low quality tier (higher CQ = smaller files)
81
+ 'low': {
82
+ 'animation': 31, # Cartoons compress well, can use higher CQ
83
+ 'low-action': 29, # Text/faces need moderate CQ for clarity
84
+ 'medium-action': 27, # Balanced CQ for general content
85
+ 'high-action': 25, # Gaming/sports need lower CQ for quality
86
+ 'default': 28 # Safe middle-ground when unsure
87
+ }
88
+ }
89
+
90
+ # Get the lookup table from config, or use our defaults
91
+ tier_cq_maps = vp.get('basic_cq_lookup_tiered', default_cq_map)
92
+
93
+ # Use the appropriate tier based on target quality
94
+ cq_lookup_table = tier_cq_maps.get(quality_tier, tier_cq_maps.get('medium', default_cq_map['medium']))
95
+
96
+ # Return the CQ for this scene type within the selected quality tier, with fallback to default
97
+ return cq_lookup_table.get(scene_type, cq_lookup_table.get('default', 25))
98
+
99
+ def get_scalers_from_pipeline(pipeline_path='services/compress/models/preprocessing_pipeline.pkl',
100
+ verbose=True, logging_enabled=True):
101
+ """
102
+ Load preprocessing pipeline and extract individual components with verbosity control.
103
+
104
+ This function loads a complete scikit-learn preprocessing pipeline and extracts
105
+ the individual components needed for VMAF prediction:
106
+ - Feature scaler for normalizing video metrics
107
+ - VMAF scaler for converting quality scores to model range
108
+ - CQ (Constant Quality) parameter bounds for optimization
109
+
110
+ The function also sets verbosity levels for all pipeline components to control
111
+ the amount of output during batch processing operations.
112
+
113
+ Pipeline Structure Expected:
114
+ - feature_scaler: StandardScaler or similar for video features
115
+ - vmaf_scaler: Custom scaler for VMAF quality scores
116
+ - cq_scaler: Custom scaler containing CQ parameter bounds
117
+
118
+ Args:
119
+ pipeline_path (str): Path to the saved preprocessing pipeline pickle file
120
+ verbose (bool): Whether individual pipeline steps should produce verbose output
121
+ logging_enabled (bool): Master logging control (overrides verbose if False)
122
+
123
+ Returns:
124
+ tuple: (pipeline_obj, feature_scaler_step, vmaf_scaler, cq_min, cq_max)
125
+ - pipeline_obj: Complete fitted preprocessing pipeline
126
+ - feature_scaler_step: Feature scaling component
127
+ - vmaf_scaler: VMAF scaling component
128
+ - cq_min: Minimum CQ value for optimization bounds
129
+ - cq_max: Maximum CQ value for optimization bounds
130
+ Returns (None, None, None, None, None) if loading fails
131
+ """
132
+
133
+ try:
134
+ from utils import ColumnDropper, VMAFScaler, TargetExtractor, CQScaler, ResolutionTransformer, FeatureScaler, FrameRateTransformer
135
+
136
+ if logging_enabled:
137
+ print(f"✅ Preprocessing classes are available through utils module")
138
+
139
+ except ImportError as e:
140
+ if logging_enabled:
141
+ print(f"❌ ERROR: Could not access preprocessing classes: {e}")
142
+ print(f" 💡 Check that utils/__init__.py properly imports data_preprocessing classes")
143
+ return None, None, None, None, None
144
+
145
+ actual_verbose = verbose and logging_enabled
146
+
147
+ pipeline_obj = None
148
+ feature_scaler_step = None
149
+ vmaf_scaler = None
150
+ cq_min_original, cq_max_original = None, None
151
+
152
+ # =================================================================
153
+ # PIPELINE FILE LOADING AND VALIDATION
154
+ # =================================================================
155
+ if not os.path.exists(pipeline_path):
156
+ if actual_verbose:
157
+ print(f"❌ ERROR: Pipeline file not found at '{pipeline_path}'")
158
+ print(f" 💡 Suggestion: Ensure preprocessing pipeline exists in /models/ directory")
159
+ return None, None, None, None, None
160
+
161
+ try:
162
+ # Load the pickled preprocessing pipeline
163
+ if actual_verbose:
164
+ print(f"📖 Loading preprocessing pipeline from: {pipeline_path}")
165
+
166
+ # Load the pickled preprocessing pipeline with custom unpickler to handle module namespace issues
167
+ class CustomUnpickler(pickle.Unpickler):
168
+ def find_class(self, module, name):
169
+ if module == '__main__':
170
+ if actual_verbose:
171
+ print(f" 🔧 Handling __main__ context for class: {name}")
172
+ # Try to get the class from utils module
173
+ try:
174
+ from utils import ColumnDropper, VMAFScaler, TargetExtractor, CQScaler, ResolutionTransformer, FeatureScaler, FrameRateTransformer
175
+ class_map = {
176
+ 'ColumnDropper': ColumnDropper,
177
+ 'VMAFScaler': VMAFScaler,
178
+ 'TargetExtractor': TargetExtractor,
179
+ 'CQScaler': CQScaler,
180
+ 'ResolutionTransformer': ResolutionTransformer,
181
+ 'FeatureScaler': FeatureScaler,
182
+ 'FrameRateTransformer': FrameRateTransformer
183
+ }
184
+ if name in class_map:
185
+ if actual_verbose:
186
+ print(f" ✅ Found {name} in utils module")
187
+ return class_map[name]
188
+ else:
189
+ if actual_verbose:
190
+ print(f" ❌ Class {name} not found in utils module")
191
+ except ImportError as e:
192
+ if actual_verbose:
193
+ print(f" ❌ Failed to import from utils: {e}")
194
+
195
+ # Handle utils.data_preprocessing module context
196
+ elif module == 'utils.data_preprocessing':
197
+ if actual_verbose:
198
+ print(f" 🔧 Handling utils.data_preprocessing context for class: {name}")
199
+ try:
200
+ from utils import ColumnDropper, VMAFScaler, TargetExtractor, CQScaler, ResolutionTransformer, FeatureScaler, FrameRateTransformer
201
+ class_map = {
202
+ 'ColumnDropper': ColumnDropper,
203
+ 'VMAFScaler': VMAFScaler,
204
+ 'TargetExtractor': TargetExtractor,
205
+ 'CQScaler': CQScaler,
206
+ 'ResolutionTransformer': ResolutionTransformer,
207
+ 'FeatureScaler': FeatureScaler,
208
+ 'FrameRateTransformer': FrameRateTransformer
209
+ }
210
+ if name in class_map:
211
+ if actual_verbose:
212
+ print(f" ✅ Found {name} in utils module")
213
+ return class_map[name]
214
+ else:
215
+ if actual_verbose:
216
+ print(f" ❌ Class {name} not found in utils module")
217
+ except ImportError as e:
218
+ if actual_verbose:
219
+ print(f" ❌ Failed to import from utils: {e}")
220
+
221
+ # For other modules, use the default behavior
222
+ if actual_verbose:
223
+ print(f" 🔧 Using default unpickler for {module}.{name}")
224
+ return super().find_class(module, name)
225
+
226
+ # Try to load with custom unpickler
227
+ try:
228
+ with open(pipeline_path, 'rb') as f:
229
+ unpickler = CustomUnpickler(f)
230
+ pipeline_obj = unpickler.load()
231
+ if actual_verbose:
232
+ print(f" ✅ Pipeline loaded with custom unpickler")
233
+ except Exception as e:
234
+ if actual_verbose:
235
+ print(f" ❌ Custom unpickler failed: {e}")
236
+ print(f" 🔧 Attempting to load with standard pickle...")
237
+
238
+ # Fallback to standard pickle loading
239
+ with open(pipeline_path, 'rb') as f:
240
+ pipeline_obj = pickle.load(f)
241
+
242
+ if actual_verbose:
243
+ print(f" ✅ Pipeline loaded with standard pickle")
244
+
245
+ if actual_verbose:
246
+ print(f"✅ Pipeline loaded successfully")
247
+
248
+ def set_pipeline_verbosity(pipeline, verbose_flag):
249
+ """
250
+ Recursively set verbose parameter for all pipeline components.
251
+
252
+ Handles nested pipelines, feature unions, column transformers,
253
+ and custom transformer classes.
254
+ """
255
+ if hasattr(pipeline, 'steps'):
256
+ # Handle sklearn Pipeline objects
257
+ for step_name, step_obj in pipeline.steps:
258
+ set_step_verbosity(step_obj, verbose_flag, step_name)
259
+ elif hasattr(pipeline, 'named_steps'):
260
+ # Handle pipelines with named step access
261
+ for step_name, step_obj in pipeline.named_steps.items():
262
+ set_step_verbosity(step_obj, verbose_flag, step_name)
263
+ else:
264
+ # Handle single transformer objects
265
+ set_step_verbosity(pipeline, verbose_flag, "pipeline")
266
+
267
+ def set_step_verbosity(step_obj, verbose_flag, step_name="unknown"):
268
+ """
269
+ Set verbosity for a single pipeline step.
270
+
271
+ Handles various transformer types and their verbosity parameters.
272
+ """
273
+ try:
274
+ # ==========================================================
275
+ # STANDARD VERBOSITY PARAMETERS
276
+ # ==========================================================
277
+ # Common verbose parameters found in sklearn and custom transformers
278
+ verbose_attrs = ['verbose', 'verbose_', 'logging_enabled']
279
+
280
+ for attr in verbose_attrs:
281
+ if hasattr(step_obj, attr):
282
+ setattr(step_obj, attr, verbose_flag)
283
+ if actual_verbose and verbose_flag:
284
+ print(f" 🔧 Set {attr}={verbose_flag} for step '{step_name}'")
285
+
286
+ # ==========================================================
287
+ # NESTED PIPELINE HANDLING
288
+ # ==========================================================
289
+ # Handle Pipeline objects nested within other pipelines
290
+ if hasattr(step_obj, 'steps') or hasattr(step_obj, 'named_steps'):
291
+ set_pipeline_verbosity(step_obj, verbose_flag)
292
+
293
+ # Handle wrapped transformers
294
+ if hasattr(step_obj, 'transformer') and step_obj.transformer is not None:
295
+ set_step_verbosity(step_obj.transformer, verbose_flag, f"{step_name}.transformer")
296
+
297
+ # ==========================================================
298
+ # COMPOSITE TRANSFORMER HANDLING
299
+ # ==========================================================
300
+ # Handle FeatureUnion and ColumnTransformer which contain lists of transformers
301
+ if hasattr(step_obj, 'transformer_list'):
302
+ for name, transformer in step_obj.transformer_list:
303
+ set_step_verbosity(transformer, verbose_flag, f"{step_name}.{name}")
304
+
305
+ # ==========================================================
306
+ # CUSTOM TRANSFORMER SPECIFIC PARAMETERS
307
+ # ==========================================================
308
+ # Handle verbosity parameters specific to our custom transformers
309
+ custom_transformer_attrs = ['debug', 'show_warnings', 'print_info']
310
+ for attr in custom_transformer_attrs:
311
+ if hasattr(step_obj, attr):
312
+ setattr(step_obj, attr, verbose_flag)
313
+ if actual_verbose and verbose_flag:
314
+ print(f" 🎛️ Set custom {attr}={verbose_flag} for step '{step_name}'")
315
+
316
+ except Exception as e:
317
+ if actual_verbose:
318
+ print(f" ⚠️ Warning: Could not set verbosity for step '{step_name}': {e}")
319
+
320
+ # Apply verbosity settings to the entire pipeline
321
+ if actual_verbose:
322
+ print(f"🔧 Setting verbosity to {actual_verbose} for all pipeline components...")
323
+
324
+ set_pipeline_verbosity(pipeline_obj, actual_verbose)
325
+
326
+ # =================================================================
327
+ # PIPELINE FITNESS VALIDATION
328
+ # =================================================================
329
+ # Check if the loaded pipeline has been fitted (trained)
330
+ # Unfitted pipelines cannot be used for transformation
331
+ try:
332
+ check_is_fitted(pipeline_obj)
333
+ if actual_verbose:
334
+ print("✅ Loaded pipeline is fitted and ready for use")
335
+ except NotFittedError:
336
+ if actual_verbose:
337
+ print("⚠️ WARNING: Loaded pipeline is NOT fitted!")
338
+ print(" 🔧 Attempting to extract components anyway for fallback creation...")
339
+ except Exception as e:
340
+ if actual_verbose:
341
+ print(f"⚠️ Warning: Could not verify pipeline fitness: {e}")
342
+
343
+ # =================================================================
344
+ # FEATURE SCALER EXTRACTION
345
+ # =================================================================
346
+ # Extract the feature scaling component used for normalizing video metrics
347
+ feature_scaler_step = None
348
+ scaler_step_name = 'feature_scaler'
349
+
350
+ # Try to find by name in named_steps
351
+ if hasattr(pipeline_obj, 'named_steps') and scaler_step_name in pipeline_obj.named_steps:
352
+ feature_scaler_step = pipeline_obj.named_steps[scaler_step_name]
353
+ if actual_verbose:
354
+ print(f"✅ Found feature scaler step: '{scaler_step_name}'")
355
+
356
+ # Fallback: search through all steps by name or type
357
+ elif hasattr(pipeline_obj, 'steps'):
358
+ for name, step in pipeline_obj.steps:
359
+ if scaler_step_name in name or isinstance(step, (StandardScaler)):
360
+ feature_scaler_step = step
361
+ if actual_verbose:
362
+ print(f"✅ Found feature scaler step: '{name}'")
363
+ break
364
+
365
+ # Create default if not found
366
+ if feature_scaler_step is None:
367
+ if actual_verbose:
368
+ print("⚠️ Warning: Could not find feature scaler step. Creating default StandardScaler...")
369
+ feature_scaler_step = StandardScaler()
370
+
371
+ # =================================================================
372
+ # VMAF SCALER EXTRACTION
373
+ # =================================================================
374
+ # Extract the VMAF scaling component for quality score normalization
375
+ vmaf_scaler = None
376
+ vmaf_scaler_step_name = 'vmaf_scaler'
377
+
378
+ if hasattr(pipeline_obj, 'named_steps') and vmaf_scaler_step_name in pipeline_obj.named_steps:
379
+ vmaf_scaler = pipeline_obj.named_steps[vmaf_scaler_step_name]
380
+ if actual_verbose:
381
+ print(f"✅ Found VMAF scaler step: '{vmaf_scaler_step_name}'")
382
+
383
+ # Create default VMAF scaler if not found
384
+ if vmaf_scaler is None:
385
+ if actual_verbose:
386
+ print("⚠️ Warning: Could not find VMAF scaler. Creating default...")
387
+
388
+ class DefaultVMAFScaler:
389
+ """Default VMAF scaler with typical VMAF range."""
390
+ def __init__(self):
391
+ self.min_val, self.max_val = 20.0, 100.0 # Typical VMAF range
392
+
393
+ vmaf_scaler = DefaultVMAFScaler()
394
+ if actual_verbose:
395
+ print(f" 📊 Using default VMAF range: {vmaf_scaler.min_val}-{vmaf_scaler.max_val}")
396
+
397
+ # =================================================================
398
+ # CQ PARAMETER BOUNDS EXTRACTION
399
+ # =================================================================
400
+ # Extract CQ (Constant Quality) parameter bounds for optimization
401
+ default_cq_min, default_cq_max = 10, 51 # Conservative CQ range
402
+ cq_min, cq_max = default_cq_min, default_cq_max
403
+ cq_scaler_step_name = 'cq_scaler'
404
+
405
+ # Try to find CQ scaler component
406
+ if hasattr(pipeline_obj, 'named_steps') and cq_scaler_step_name in pipeline_obj.named_steps:
407
+ cq_scaler = pipeline_obj.named_steps[cq_scaler_step_name]
408
+
409
+ # Check for CQ bounds in various attribute naming conventions
410
+ if hasattr(cq_scaler, 'min_cq') and hasattr(cq_scaler, 'max_cq'):
411
+ cq_min, cq_max = cq_scaler.min_cq, cq_scaler.max_cq
412
+ if actual_verbose:
413
+ print(f"✅ Found CQ range in step '{cq_scaler_step_name}': {cq_min}-{cq_max}")
414
+ elif hasattr(cq_scaler, 'min_val') and hasattr(cq_scaler, 'max_val'):
415
+ cq_min, cq_max = cq_scaler.min_val, cq_scaler.max_val
416
+ if actual_verbose:
417
+ print(f"✅ Found CQ range in scaler attributes: {cq_min}-{cq_max}")
418
+
419
+ if actual_verbose:
420
+ print(f"📊 Using CQ optimization range: {cq_min}-{cq_max}")
421
+ print(f" 💡 This range will constrain the binary search for optimal quality parameters")
422
+
423
+ return pipeline_obj, feature_scaler_step, vmaf_scaler, int(cq_min), int(cq_max)
424
+
425
+ except Exception as e:
426
+ if actual_verbose:
427
+ print(f"❌ ERROR: Failed to load or extract from pipeline '{pipeline_path}': {e}")
428
+ print("🔍 Detailed error information:")
429
+ traceback.print_exc()
430
+ return None, None, None, None, None
431
+
432
+ def load_encoding_resources(config, logging_enabled=True):
433
+ """
434
+
435
+ 1. Scene classifier AI model - to identify content type (animation, gaming, etc.)
436
+ 2. Preprocessing pipeline - to prepare video frames for the AI model
437
+ 3. GPU/CPU device selection - for running the AI inference
438
+
439
+
440
+ Args:
441
+ config (dict): Configuration containing model file paths
442
+ logging_enabled (bool): Whether to print loading status messages
443
+
444
+ Returns:
445
+ tuple: (scene_model, preprocessing_pipeline, device) ready for encoding
446
+ """
447
+ # Get model file paths from config
448
+ model_paths = config.get('model_paths', {})
449
+ scene_model_path = model_paths.get('scene_classifier_model', "services/compress/models/scene_classifier_model.pth")
450
+ preprocessing_pipeline_path = model_paths.get('preprocessing_pipeline', "services/compress/models/preprocessing_pipeline.pkl")
451
+
452
+ # Choose GPU if available, otherwise use CPU
453
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
454
+
455
+ # Load scene classifier AI model
456
+ model_state_dict, available_metrics, class_mapping = load_scene_classifier_model(scene_model_path, device, logging_enabled)
457
+ print(f" ✅ Scene classifier model loaded from {scene_model_path} on {device}")
458
+ scene_classifier_model = CombinedModel(num_classes=len(class_mapping), metrics_dim=len(available_metrics))
459
+ scene_classifier_model.load_state_dict(model_state_dict)
460
+ scene_classifier_model.to(device)
461
+ scene_classifier_model.eval()
462
+
463
+ pipeline_obj, feature_scaler_step, vmaf_scaler, cq_min, cq_max = get_scalers_from_pipeline(preprocessing_pipeline_path, verbose=logging_enabled, logging_enabled=logging_enabled)
464
+ print(f" ✅ Preprocessing pipeline loaded from {preprocessing_pipeline_path}")
465
+
466
+ return {
467
+ "scene_classifier_model": scene_classifier_model,
468
+ "available_metrics": available_metrics,
469
+ "device": device,
470
+ "class_mapping": class_mapping,
471
+ "pipeline_obj": pipeline_obj,
472
+ "feature_scaler_step": feature_scaler_step,
473
+ "vmaf_scaler": vmaf_scaler,
474
+ "cq_min": cq_min,
475
+ "cq_max": cq_max
476
+ }
477
+
478
+ def ai_encoding(scene_metadata, config, resources, target_vmaf=None, target_quality_level=None, logging_enabled=True):
479
+ """
480
+
481
+ 1. Use AI to classify the scene content (animation, gaming, text, etc.)
482
+ 2. Look up a pre-determined CQ value from a table based on scene type AND quality tier
483
+ 3. Encode the scene once with that CQ value
484
+
485
+ - High quality tier (VMAF ~95): Lower CQ values for better quality
486
+ - Medium quality tier (VMAF ~90): Balanced CQ values
487
+ - Low quality tier (VMAF ~85): Higher CQ values for smaller files
488
+
489
+ - Runs multiple encoding attempts with different CQ values
490
+ - Uses AI to predict VMAF quality for each attempt
491
+ - Uses binary search to find the optimal CQ
492
+
493
+ Args:
494
+ scene_metadata (dict): Contains scene file path, duration, etc.
495
+ config (dict): Configuration settings for encoding
496
+ resources (dict): Pre-loaded AI models and preprocessing tools
497
+ target_vmaf (float): Target quality used to select appropriate quality tier
498
+ target_quality_level (str): Desired quality level ('High', 'Medium', 'Low') used for CQ lookup
499
+ logging_enabled (bool): Whether to print progress messages
500
+
501
+ Returns:
502
+ tuple: (success_status, scene_data_dict)
503
+ - success_status: True if encoding succeeded, False otherwise
504
+ - scene_data_dict: Contains encoding results, file sizes, CQ used, etc.
505
+ """
506
+ logging_enabled=True
507
+
508
+ if not scene_metadata:
509
+ return None, {
510
+ 'scene_number': 0,
511
+ 'encoding_success': False,
512
+ 'error_reason': 'Scene metadata is None or empty',
513
+ 'processing_time_seconds': 0.0,
514
+ 'encoded_path': None,
515
+ 'path': None
516
+ }
517
+
518
+ def safe_float(value, default=0.0):
519
+ """Convert value to float, return default if invalid"""
520
+ if value is None: return default
521
+ try:
522
+ result = float(value)
523
+ return result if not (result != result) else default
524
+ except (TypeError, ValueError):
525
+ return default
526
+
527
+ def safe_positive_float(value, default=1.0):
528
+ """Convert to float and ensure it's positive"""
529
+ result = safe_float(value, default)
530
+ return max(result, 0.1)
531
+
532
+ scene_path = scene_metadata.get('path')
533
+ scene_number = int(safe_float(scene_metadata.get('scene_number', 1), 1))
534
+ start_time = safe_float(scene_metadata.get('start_time'), 0.0)
535
+ end_time = safe_float(scene_metadata.get('end_time'), 0.0)
536
+ scene_duration = safe_positive_float(scene_metadata.get('duration'), 1.0)
537
+
538
+ if end_time <= start_time or scene_duration <= 0:
539
+ if end_time > start_time:
540
+ scene_duration = end_time - start_time
541
+ else:
542
+ try:
543
+ cmd = ['ffprobe', '-v', 'quiet', '-show_entries', 'format=duration', '-of', 'csv=p=0', scene_path]
544
+ result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=10)
545
+ if result.returncode == 0 and result.stdout.strip():
546
+ scene_duration = safe_positive_float(result.stdout.strip(), 1.0)
547
+ end_time = start_time + scene_duration
548
+ else:
549
+ scene_duration = 1.0
550
+ except Exception:
551
+ scene_duration = 1.0
552
+
553
+ is_very_short_scene = scene_duration < 1.0
554
+ if is_very_short_scene and logging_enabled:
555
+ print(f" ⚠️ Very short scene detected ({scene_duration:.1f}s) - using special handling")
556
+
557
+ original_video_metadata = scene_metadata.get('original_video_metadata', {})
558
+
559
+ if logging_enabled:
560
+ print(f"\n🎬 Processing Scene {scene_number}")
561
+ print(f" 📁 File: {os.path.basename(scene_path) if scene_path else 'None'}")
562
+ print(f" ⏱️ Timing: {start_time:.1f}s - {end_time:.1f}s (duration: {scene_duration:.1f}s)")
563
+
564
+ if not scene_path or not os.path.exists(scene_path) or os.path.getsize(scene_path) == 0:
565
+ return None, {
566
+ 'scene_number': scene_number,
567
+ 'encoding_success': False,
568
+ 'error_reason': 'Scene file is missing, empty, or inaccessible',
569
+ 'processing_time_seconds': 0.0,
570
+ 'encoded_path': None,
571
+ 'path': scene_path
572
+ }
573
+
574
+ processing_start_time = time.time()
575
+
576
+ # Get temporary directory for intermediate files
577
+ temp_dir = config.get('directories', {}).get('temp_dir', './videos/temp_scenes')
578
+
579
+ if not target_quality_level:
580
+ target_quality_level = original_video_metadata.get('target_quality') or config.get('video_processing', {}).get('target_quality')
581
+ target_vmaf = safe_float(target_vmaf or original_video_metadata.get('target_vmaf') or
582
+ config.get('video_processing', {}).get('target_vmaf', 0.0), 0.0)
583
+
584
+ original_codec = original_video_metadata.get('original_codec', 'unknown')
585
+ target_codec_from_part1 = original_video_metadata.get('target_codec', 'auto')
586
+ current_codec = original_video_metadata.get('codec', original_codec)
587
+ config_codec = config.get('video_processing', {}).get('codec', 'auto')
588
+
589
+ if target_codec_from_part1 and target_codec_from_part1 != 'auto':
590
+ codec = target_codec_from_part1
591
+ elif config_codec != 'auto':
592
+ codec = config_codec
593
+ else:
594
+ codec_upgrade_map = {'h264': 'av1_nvenc', 'hevc': 'av1_nvenc', 'vp9': 'av1_nvenc', 'av1': 'av1_nvenc'}
595
+ codec = codec_upgrade_map.get(current_codec.lower(), 'av1_nvenc')
596
+
597
+ if logging_enabled:
598
+ print(f" 🎥 Selected Codec: {codec}")
599
+
600
+ # STEP 1: BASIC ANALYSIS - Classify scene and extract video features in one call
601
+ if logging_enabled:
602
+ print(f" 🤖 Running scene classification and feature extraction...")
603
+
604
+ scene_type = 'default'
605
+ confidence_score = 0.0
606
+ video_features = {}
607
+ detailed_results = {}
608
+ try:
609
+ classification_result = classify_scene_from_path(
610
+ scene_path=scene_path,
611
+ temp_dir=temp_dir,
612
+ scene_classifier_model=resources['scene_classifier_model'],
613
+ available_metrics=resources['available_metrics'],
614
+ device=resources['device'],
615
+ metrics_scaler=resources['feature_scaler_step'],
616
+ class_mapping=resources['class_mapping'],
617
+ logging_enabled=logging_enabled,
618
+ )
619
+
620
+ # Handle the tuple return from classify_scene_from_path (now returns 3 items)
621
+ if isinstance(classification_result, tuple) and len(classification_result) == 3:
622
+ scene_type, detailed_results, video_features = classification_result
623
+ confidence_score = detailed_results.get('confidence_score', 0.0)
624
+ elif isinstance(classification_result, tuple) and len(classification_result) == 2:
625
+ # Fallback for older return format
626
+ scene_type, detailed_results = classification_result
627
+ confidence_score = detailed_results.get('confidence_score', 0.0)
628
+ video_features = {}
629
+ elif isinstance(classification_result, dict):
630
+ # Fallback if it returns a dict instead of tuple
631
+ scene_type = classification_result.get('scene_type', 'default')
632
+ confidence_score = classification_result.get('confidence_score', 0.0)
633
+ video_features = {}
634
+ else:
635
+ # Unknown return type, use defaults
636
+ scene_type = 'default'
637
+ confidence_score = 0.0
638
+ video_features = {}
639
+
640
+ if logging_enabled:
641
+ print(f" 🎭 Scene classified as: '{scene_type}' (Confidence: {confidence_score:.2f})")
642
+ if video_features:
643
+ print(f" 📊 Video features extracted: {len(video_features)} metrics")
644
+
645
+ except Exception as e:
646
+ if logging_enabled:
647
+ print(f" ❌ Scene classification failed: {e}")
648
+ traceback.print_exc()
649
+
650
+ # Map the scene type to lookup table key
651
+ original_scene_type = scene_type
652
+ mapped_scene_type = map_scene_type_to_lookup_key(scene_type)
653
+
654
+ if logging_enabled and original_scene_type != mapped_scene_type:
655
+ print(f" 🔄 Mapped scene type '{original_scene_type}' -> '{mapped_scene_type}' for CQ lookup")
656
+
657
+ # Get quality tier information for logging
658
+ print(f" 🎯 Target quality level: {target_quality_level}")
659
+ # If only quality level provided, derive indicative VMAF for logging purposes
660
+ if target_quality_level and not target_vmaf:
661
+ target_vmaf = get_target_vmaf_from_quality(target_quality_level)
662
+ if logging_enabled:
663
+ print(f" ℹ️ Indicative VMAF from quality level '{target_quality_level}': {target_vmaf}")
664
+
665
+ # Get CQ from lookup table using mapped scene type and target quality/VMAF
666
+ base_cq = get_cq_from_lookup_table(
667
+ mapped_scene_type,
668
+ config,
669
+ target_vmaf=target_vmaf,
670
+ target_quality_level=target_quality_level,
671
+ )
672
+
673
+ # Log quality tier and CQ selection
674
+ if logging_enabled:
675
+ # Determine label for logging
676
+ if target_quality_level:
677
+ tier_label = str(target_quality_level).upper()
678
+ else:
679
+ if target_vmaf >= 93:
680
+ tier_label = "HIGH"
681
+ elif target_vmaf >= 88:
682
+ tier_label = "MEDIUM"
683
+ else:
684
+ tier_label = "LOW"
685
+
686
+ print(f" 🎯 Using quality tier: {tier_label} (Indicative VMAF: {target_vmaf if target_vmaf else 'n/a'})")
687
+ print(f" 🎚️ Base CQ from lookup table for '{mapped_scene_type}' at {tier_label} quality: {base_cq}")
688
+
689
+ # Apply conservative adjustment from config
690
+ conservative_cq_adjustment = safe_float(config.get('video_processing', {}).get('conservative_cq_adjustment', 2), 2)
691
+ final_cq = min(base_cq + conservative_cq_adjustment, 51.0)
692
+ if logging_enabled:
693
+ print(f" 🔧 Applied conservative adjustment: +{conservative_cq_adjustment} -> Final CQ: {final_cq}")
694
+
695
+ # Create a placeholder scene_data object
696
+ scene_data = {
697
+ 'path': scene_path,
698
+ 'scene_number': scene_number,
699
+ 'start_time': start_time,
700
+ 'end_time': end_time,
701
+ 'duration': scene_duration,
702
+ 'original_video_metadata': original_video_metadata,
703
+ 'scene_type': original_scene_type, # Keep original scene type from classifier
704
+ 'mapped_scene_type': mapped_scene_type, # Add mapped scene type for CQ lookup
705
+ 'confidence_score': confidence_score,
706
+ 'optimal_cq': base_cq,
707
+ 'adjusted_cq': final_cq,
708
+ 'final_adjusted_cq': final_cq,
709
+ 'codec_selection_process': {'final_selected_codec': codec},
710
+ 'model_training_data': {}, # Placeholder
711
+ 'target_quality_level': target_quality_level,
712
+ 'base_cq_for_quality': base_cq,
713
+ }
714
+
715
+ if scene_duration < 1.0:
716
+ output_scene_path = os.path.join(
717
+ temp_dir,
718
+ f"encoded_scene_{scene_number:03d}_{start_time:.1f}s-{end_time:.1f}s_{codec.lower()}.mp4"
719
+ )
720
+ else:
721
+ output_scene_path = os.path.join(
722
+ temp_dir,
723
+ f"encoded_scene_{scene_number:03d}_{start_time:.0f}s-{end_time:.0f}s_{codec.lower()}.mp4"
724
+ )
725
+ os.makedirs(temp_dir, exist_ok=True)
726
+
727
+ size_increase_protection = config.get('video_processing', {}).get('size_increase_protection', True)
728
+ max_encoding_retries = int(safe_float(config.get('video_processing', {}).get('max_encoding_retries', 2), 2))
729
+
730
+ encoding_start_time = time.time()
731
+ encoded_path = None
732
+ encoding_time = 0
733
+
734
+ if size_increase_protection:
735
+ if logging_enabled:
736
+ print(f" 🛡️ Size increase protection enabled")
737
+ try:
738
+ encoded_path, encoding_time = encode_scene_with_size_check(
739
+ scene_path=scene_path,
740
+ output_path=output_scene_path,
741
+ codec=codec,
742
+ adjusted_cq=final_cq,
743
+ content_type=scene_type,
744
+ contrast_value=0.5,
745
+ max_retries=max_encoding_retries,
746
+ logging_enabled=logging_enabled
747
+ )
748
+ except Exception as e:
749
+ if logging_enabled:
750
+ print(f" ❌ Size-protected encoding failed: {e}")
751
+ else:
752
+ if logging_enabled:
753
+ print(f" ⚡ Standard encoding (size protection disabled)")
754
+ try:
755
+ _, encoding_time = encode_video(
756
+ input_path=scene_path,
757
+ output_path=output_scene_path,
758
+ codec=codec,
759
+ rate=final_cq,
760
+ scene_type=scene_type,
761
+ contrast_value=0.5, # Default contrast
762
+ logging_enabled=logging_enabled
763
+ )
764
+ encoded_path = output_scene_path
765
+ except Exception as e:
766
+ if logging_enabled:
767
+ print(f" ❌ Standard encoding failed: {e}")
768
+
769
+ # Final update of scene_data
770
+ encoding_success = bool(encoded_path and os.path.exists(encoded_path) and os.path.getsize(encoded_path) > 0)
771
+ scene_data['encoding_success'] = encoding_success
772
+ scene_data['encoded_path'] = encoded_path if encoding_success else None
773
+ scene_data['encoding_time'] = safe_float(encoding_time, 0)
774
+ scene_data['processing_time_seconds'] = time.time() - processing_start_time
775
+
776
+ # Add file size information and codec details
777
+ if encoding_success:
778
+ # Calculate file sizes in MB
779
+ input_file_size = os.path.getsize(scene_path) if os.path.exists(scene_path) else 0
780
+ output_file_size = os.path.getsize(encoded_path) if os.path.exists(encoded_path) else 0
781
+
782
+ scene_data['input_size_mb'] = input_file_size / (1024 * 1024)
783
+ scene_data['encoded_file_size_mb'] = output_file_size / (1024 * 1024)
784
+
785
+ # Calculate compression ratio
786
+ if input_file_size > 0:
787
+ compression_ratio = ((output_file_size - input_file_size) / input_file_size) * 100
788
+ scene_data['compression_ratio'] = compression_ratio
789
+ else:
790
+ scene_data['compression_ratio'] = 0
791
+
792
+ # Store codec information for filename generation
793
+ scene_data['codec_used'] = codec
794
+ else:
795
+ scene_data['input_size_mb'] = 0
796
+ scene_data['encoded_file_size_mb'] = 0
797
+ scene_data['compression_ratio'] = 0
798
+ scene_data['codec_used'] = 'unknown'
799
+
800
+
801
+ scene_data['model_training_data'] = {
802
+ 'raw_video_features': video_features,
803
+ 'processed_video_features': {},
804
+ 'vmaf_model_features': {},
805
+ 'scene_classifier_features': video_features,
806
+ 'scene_classifier_probabilities': detailed_results,
807
+ 'processing_timings': {
808
+ 'total_processing_time': time.time() - processing_start_time,
809
+ 'encoding_time': safe_float(encoding_time, 0) if 'encoding_time' in locals() else 0
810
+ }
811
+ }
812
+
813
+ if encoding_success:
814
+ if logging_enabled:
815
+ print(f" ✅ Scene {scene_number} encoded successfully.")
816
+ return encoded_path, scene_data
817
+ else:
818
+ if logging_enabled:
819
+ print(f" ❌ Encoding failed for scene {scene_number}.")
820
+ scene_data['error_reason'] = 'Encoding process failed or produced an empty file'
821
+ return None, scene_data
822
+
823
+ def map_scene_type_to_lookup_key(scene_type):
824
+ """
825
+ Convert AI scene classifier output to CQ lookup table keys.
826
+
827
+ The AI scene classifier returns descriptive names like:
828
+ - 'Screen Content / Text'
829
+ - 'Animation / Cartoon / Rendered Graphics'
830
+ - 'Faces / People'
831
+ - 'Gaming Content'
832
+ - 'other'
833
+ - 'unclear'
834
+
835
+ But our CQ lookup table uses simpler keys:
836
+ - 'animation' (for cartoons/rendered content)
837
+ - 'low-action' (for text/faces - less motion)
838
+ - 'medium-action' (for general content)
839
+ - 'high-action' (for gaming/sports - lots of motion)
840
+ - 'default' (fallback for unclear content)
841
+
842
+ Args:
843
+ scene_type (str): Output from AI scene classifier
844
+
845
+ Returns:
846
+ str: Corresponding lookup table key
847
+ """
848
+ scene_lower = scene_type.lower() if scene_type else 'default'
849
+
850
+ scene_mapping = {
851
+ 'screen content / text': 'low-action', # Text doesn't need high quality
852
+ 'animation / cartoon / rendered graphics': 'animation', # Cartoons compress well
853
+ 'faces / people': 'low-action', # Faces need clarity but less motion
854
+ 'gaming content': 'high-action', # Games have lots of motion/detail
855
+ 'other': 'medium-action', # General content gets medium quality
856
+ 'unclear': 'default', # When unsure, use safe default
857
+ 'default': 'default'
858
+ }
859
+
860
+ if scene_lower in scene_mapping:
861
+ return scene_mapping[scene_lower]
862
+
863
+ for key, value in scene_mapping.items():
864
+ if key in scene_lower or scene_lower in key:
865
+ return value
866
+
867
+ return 'default'
868
+
869
+ def get_target_vmaf_from_quality(quality_level):
870
+ """
871
+ Map quality level to target VMAF score for Basic AI encoding.
872
+
873
+
874
+ Quality Level Mappings:
875
+ - High: 95 VMAF (Maximum quality preservation)
876
+ - Medium: 90 VMAF (Balanced quality and file size)
877
+ - Low: 85 VMAF (Aggressive compression for smaller files)
878
+
879
+ Args:
880
+ quality_level (str): Target quality level ('High', 'Medium', 'Low')
881
+
882
+ Returns:
883
+ float: Target VMAF score for the specified quality level
884
+ """
885
+ quality_vmaf_map = {
886
+ 'High': 95.0,
887
+ 'Medium': 90.0,
888
+ 'Low': 85.0
889
+ }
890
+
891
+ return quality_vmaf_map.get(quality_level, 90.0)
892
+
893
+
services/compress/models/preprocessing_pipeline.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:341e6c97c4092410edfd7ec9ba64f4d8916e397768138a345530ee2bcca95333
3
+ size 3594
services/compress/models/scene_classifier_model.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c31861eae4d5e39dd4e54994e44bc34a099342f237ee96548d6d663f3f10e3df
3
+ size 11130108
services/compress/scene_detector.py ADDED
@@ -0,0 +1,614 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import subprocess
4
+ from utils.fast_scene_detect import adaptive_scene_detection_check
5
+ from utils.split_video_into_scenes import split_video_into_scenes
6
+ from utils.video_utils import get_keyframes
7
+
8
+ def scene_detection(video_metadata):
9
+ """
10
+ Part 2: Scene detection based on video length using metadata from part 1.
11
+
12
+ Args:
13
+ video_metadata (dict): Metadata from part1_pre_processing containing:
14
+ - 'path': video file path
15
+ - 'codec': current video codec
16
+ - 'original_codec': original codec (if reencoded)
17
+ - 'target_codec': target codec for encoding (NEW)
18
+ - 'duration': video duration in seconds
19
+ - 'was_reencoded': boolean
20
+ - 'target_vmaf': target VMAF score
21
+ - 'target_quality': target quality level
22
+ - 'processing_info': processing details
23
+
24
+ Returns:
25
+ list: List of scene metadata dictionaries, each containing:
26
+ - 'path': scene file path
27
+ - 'scene_number': scene index (1-based)
28
+ - 'start_time': scene start time in seconds
29
+ - 'end_time': scene end time in seconds
30
+ - 'duration': scene duration in seconds
31
+ - 'original_video_metadata': complete metadata from part 1
32
+ """
33
+ # Load configuration
34
+ try:
35
+ with open('services/compress/config.json', 'r') as f:
36
+ config = json.load(f)
37
+ print("✅ Configuration loaded successfully")
38
+ except FileNotFoundError:
39
+ print("⚠️ Config file not found, using default configuration")
40
+ config = {
41
+ 'directories': {
42
+ 'temp_dir': './videos/temp_scenes',
43
+ 'output_dir': './output'
44
+ },
45
+ 'video_processing': {
46
+ 'SHORT_VIDEO_THRESHOLD': 20,
47
+ 'target_vmaf': 93.0
48
+ },
49
+ 'scene_detection': {
50
+ 'enable_time_based_fallback': True,
51
+ 'time_based_scene_duration': 60
52
+ }
53
+ }
54
+
55
+ # Extract metadata
56
+ video_path = video_metadata['path']
57
+ duration = video_metadata['duration']
58
+ codec = video_metadata['codec']
59
+ original_codec = video_metadata.get('original_codec', codec)
60
+ target_codec = video_metadata.get('target_codec', 'auto')
61
+ print("***********target_codec", target_codec) # ✅ NEW: Extract target codec
62
+ was_reencoded = video_metadata.get('was_reencoded', False)
63
+ target_vmaf = video_metadata.get('target_vmaf', 93.0)
64
+ target_quality = video_metadata.get('target_quality', 'Medium')
65
+
66
+ print(f"🎬 Processing video: {os.path.basename(video_path)}")
67
+ print(f" ⏱️ Duration: {duration:.1f}s, Codec: {codec}, Reencoded: {was_reencoded}")
68
+ print(f" 🎯 Target: {target_quality} (VMAF: {target_vmaf})")
69
+ print(f" 🎥 Codec flow: {original_codec} → {codec} → {target_codec}") # ✅ NEW: Show codec flow
70
+
71
+ # Get processing configuration
72
+ video_processing_config = config.get('video_processing', {})
73
+ short_video_threshold = video_processing_config.get('SHORT_VIDEO_THRESHOLD', 20)
74
+ # Get directories from config only
75
+ temp_dir = config.get('directories', {}).get('temp_dir', './videos/temp_scenes')
76
+
77
+ if duration <= short_video_threshold:
78
+ print(f"📏 Video is shorter than {short_video_threshold}s. Treating as a single scene.")
79
+ return [{
80
+ 'path': video_metadata['path'],
81
+ 'scene_number': 1,
82
+ 'start_time': 0.0,
83
+ 'end_time': duration,
84
+ 'duration': duration,
85
+ 'original_video_metadata': video_metadata # ✅ Pass complete metadata including target_codec
86
+ }]
87
+ scene_config = config.get('scene_detection', {})
88
+ detection_mode = scene_config.get('mode', 'adaptive').lower()
89
+
90
+ print(f" 🔍 Scene detection mode: {detection_mode}")
91
+
92
+ if detection_mode == 'force_time_based':
93
+ # Force time-based splitting regardless of content
94
+ force_duration = scene_config.get('force_time_based_duration', 10)
95
+ print(f" ⏰ Forcing time-based splitting with {force_duration}s segments")
96
+ return force_time_based_splitting(video_path, temp_dir, force_duration, duration, video_metadata)
97
+
98
+ elif detection_mode == 'adaptive':
99
+ # Original adaptive scene detection logic
100
+ print(f" 🔍 Running adaptive scene detection...")
101
+ has_multiple_scenes, estimated_scenes, _ = adaptive_scene_detection_check(
102
+ video_path, duration, logging_enabled=True
103
+ )
104
+
105
+ if has_multiple_scenes:
106
+ print(f" ✅ Multiple scenes detected ({estimated_scenes} estimated). Proceeding with full scene splitting.")
107
+ scene_files, _ = split_video_into_scenes(video_path, temp_dir)
108
+
109
+ if scene_files and len(scene_files) > 1:
110
+ print(f" 📄 Created {len(scene_files)} scene files")
111
+ return create_scene_metadata_from_files(scene_files, video_metadata, config)
112
+ else:
113
+ print(f" ⚠️ Scene splitting failed or found only 1 scene. Falling back to time-based splitting.")
114
+ return time_based_splitting(video_path, temp_dir, config, duration, video_metadata)
115
+ else:
116
+ print(f" 📏 Single scene detected.")
117
+ enable_fallback = scene_config.get('enable_time_based_fallback', False)
118
+
119
+ if enable_fallback:
120
+ print(f" 🔄 Time-based fallback enabled. Attempting time-based scene splitting...")
121
+ return time_based_splitting(video_path, temp_dir, config, duration, video_metadata)
122
+ else:
123
+ print(f" ✅ Using single scene (time-based fallback disabled)")
124
+ return [{
125
+ 'path': video_path,
126
+ 'scene_number': 1,
127
+ 'start_time': 0.0,
128
+ 'end_time': duration,
129
+ 'duration': duration,
130
+ 'original_video_metadata': video_metadata # ✅ Pass complete metadata including target_codec
131
+ }]
132
+
133
+ else:
134
+ print(f" ❌ Unknown scene detection mode: {detection_mode}. Falling back to adaptive.")
135
+ # Fallback to adaptive mode
136
+ print(f" 🔍 Running adaptive scene detection...")
137
+ has_multiple_scenes, estimated_scenes, _ = adaptive_scene_detection_check(
138
+ video_path, duration, logging_enabled=True
139
+ )
140
+
141
+ if has_multiple_scenes:
142
+ print(f" ✅ Multiple scenes detected ({estimated_scenes} estimated). Proceeding with full scene splitting.")
143
+ scene_files, _ = split_video_into_scenes(video_path, temp_dir)
144
+
145
+ if scene_files and len(scene_files) > 1:
146
+ print(f" 📄 Created {len(scene_files)} scene files")
147
+ return create_scene_metadata_from_files(scene_files, video_metadata, config)
148
+ else:
149
+ print(f" ⚠️ Scene splitting failed or found only 1 scene. Falling back to time-based splitting.")
150
+ return time_based_splitting(video_path, temp_dir, config, duration, video_metadata)
151
+ else:
152
+ print(f" 📏 Single scene detected. Using single scene.")
153
+ return [{
154
+ 'path': video_path,
155
+ 'scene_number': 1,
156
+ 'start_time': 0.0,
157
+ 'end_time': duration,
158
+ 'duration': duration,
159
+ 'original_video_metadata': video_metadata # ✅ Pass complete metadata including target_codec
160
+ }]
161
+
162
+ def force_time_based_splitting(video_path, temp_dir, segment_duration, total_duration, original_metadata, logging_enabled=True):
163
+ """
164
+ Force time-based splitting with keyframe-aligned boundaries to prevent stuttering.
165
+
166
+ Args:
167
+ video_path (str): Path to the video file
168
+ temp_dir (str): Temporary directory for scene files
169
+ segment_duration (float): Target duration for each segment in seconds
170
+ total_duration (float): Total video duration in seconds
171
+ original_metadata (dict): Complete metadata from Part 1
172
+ logging_enabled (bool): Enable detailed logging
173
+
174
+ Returns:
175
+ list: List of scene metadata dictionaries
176
+ """
177
+ if logging_enabled:
178
+ print(f"\n--- Force time-based splitting: {segment_duration}s segments ---")
179
+ print(f" 🎥 Source codec: {original_metadata.get('codec', 'unknown')}")
180
+ print(f" ⏱️ Total duration: {total_duration:.1f}s")
181
+ print(f" ✂️ Segment duration: {segment_duration}s")
182
+
183
+ # Get keyframes for precise cutting
184
+ try:
185
+ keyframes = get_keyframes(video_path)
186
+ if logging_enabled:
187
+ print(f" 🔑 Found {len(keyframes)} keyframes for alignment")
188
+ except Exception as e:
189
+ # Try alternative import path
190
+ try:
191
+ keyframes = get_keyframes(video_path)
192
+ if logging_enabled:
193
+ print(f" 🔑 Found {len(keyframes)} keyframes for alignment")
194
+ except Exception as e2:
195
+ keyframes = []
196
+ if logging_enabled:
197
+ print(f" ⚠️ Could not get keyframes: {e}")
198
+ print(f" 📐 Using time-based boundaries (may cause stuttering)")
199
+
200
+ # Calculate scene boundaries, preferring keyframe alignment
201
+ scene_boundaries = []
202
+ current_start = 0
203
+
204
+ while current_start < total_duration:
205
+ target_end = min(current_start + segment_duration, total_duration)
206
+
207
+ # If we have keyframes, find the closest keyframe to target_end
208
+ if keyframes and target_end < total_duration:
209
+ # Find keyframe closest to target_end
210
+ closest_keyframe = min(keyframes, key=lambda kf: abs(kf - target_end))
211
+
212
+ # Only use keyframe if it's reasonably close (within 2 seconds)
213
+ if abs(closest_keyframe - target_end) <= 2.0 and closest_keyframe > current_start:
214
+ scene_end = closest_keyframe
215
+ else:
216
+ scene_end = target_end
217
+ else:
218
+ scene_end = target_end
219
+
220
+ # Handle very short final segments (< 2 seconds) by merging with previous scene
221
+ remaining_duration = total_duration - scene_end
222
+ if remaining_duration > 0 and remaining_duration < 2.0 and scene_boundaries:
223
+ # Extend current scene to end of video instead of creating tiny final scene
224
+ scene_end = total_duration
225
+ if logging_enabled:
226
+ print(f" 🔧 Merging final {remaining_duration:.1f}s with current scene to avoid stuttering")
227
+
228
+ scene_boundaries.append((current_start, scene_end))
229
+ current_start = scene_end
230
+
231
+ if logging_enabled:
232
+ print(f" 📊 Will create {len(scene_boundaries)} segments:")
233
+ for i, (start, end) in enumerate(scene_boundaries):
234
+ print(f" Segment {i+1}: {start:.1f}s - {end:.1f}s (duration: {end-start:.1f}s)")
235
+
236
+ # If only one scene, return original video
237
+ if len(scene_boundaries) <= 1:
238
+ if logging_enabled:
239
+ print(f" 📏 Video shorter than segment duration, using as single scene")
240
+ return [{
241
+ 'path': video_path,
242
+ 'scene_number': 1,
243
+ 'start_time': 0.0,
244
+ 'end_time': total_duration,
245
+ 'duration': total_duration,
246
+ 'original_video_metadata': original_metadata
247
+ }]
248
+
249
+ # Create output directory
250
+ os.makedirs(temp_dir, exist_ok=True)
251
+
252
+ # Choose scene file extension based on source codec
253
+ source_codec = original_metadata.get('codec', 'h264').lower()
254
+ if source_codec in ['ffv1', 'prores', 'dnxhd']:
255
+ scene_extension = '.mkv' # Use MKV for lossless codecs
256
+ elif source_codec in ['av1', 'vp9']:
257
+ scene_extension = '.webm' # Use WebM for modern codecs
258
+ else:
259
+ scene_extension = '.mp4' # Default to MP4 for compatibility
260
+
261
+ if logging_enabled:
262
+ print(f" 📦 Using {scene_extension} container for {source_codec} codec")
263
+
264
+ # Split video and create metadata
265
+ scene_metadata_list = []
266
+
267
+ for i, (start_time_sec, end_time_sec) in enumerate(scene_boundaries):
268
+ scene_number = i + 1
269
+ scene_filename = f"scene_{scene_number:03d}_time_{start_time_sec:.0f}s{scene_extension}"
270
+ scene_path = os.path.join(temp_dir, scene_filename)
271
+
272
+ duration_scene = end_time_sec - start_time_sec
273
+
274
+ # FFmpeg command with precise cutting to prevent frame alignment issues
275
+ # Use input seeking for efficiency and output seeking for precision
276
+ cmd = [
277
+ 'ffmpeg', '-y',
278
+ '-ss', str(start_time_sec), # Input seeking for efficiency
279
+ '-i', video_path,
280
+ '-t', str(duration_scene), # Precise duration
281
+ '-avoid_negative_ts', 'make_zero',
282
+ '-fflags', '+genpts', # Generate presentation timestamps
283
+ '-map', '0:v:0', # Map video stream explicitly
284
+ '-map', '0:a?', # Map audio if exists (optional)
285
+ ]
286
+
287
+ # Add codec-specific format options before encoding
288
+ format_options = []
289
+ if source_codec == 'ffv1':
290
+ format_options.extend(['-f', 'matroska'])
291
+ elif source_codec in ['av1', 'vp9']:
292
+ format_options.extend(['-f', 'webm'])
293
+
294
+ # Always use fast re-encoding to ensure clean cuts and prevent stuttering
295
+ # Stream copy can cause artifacts at scene boundaries even with keyframe alignment
296
+ cmd.extend([
297
+ '-c:v', 'libx264',
298
+ '-preset', 'ultrafast',
299
+ '-crf', '18',
300
+ '-c:a', 'copy'
301
+ ])
302
+ cmd.extend(format_options) # Add format options
303
+ cmd.append(scene_path) # Add output file path
304
+
305
+ if logging_enabled:
306
+ print(f" ✂️ Creating segment {scene_number}: {scene_filename}")
307
+
308
+ try:
309
+ # Execute FFmpeg command
310
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
311
+
312
+ if result.returncode == 0:
313
+ # Verify file exists and has content
314
+ if os.path.exists(scene_path) and os.path.getsize(scene_path) > 0:
315
+ file_size_mb = os.path.getsize(scene_path) / (1024 * 1024)
316
+
317
+ scene_metadata = {
318
+ 'path': scene_path,
319
+ 'scene_number': scene_number,
320
+ 'start_time': start_time_sec,
321
+ 'end_time': end_time_sec,
322
+ 'duration': duration_scene,
323
+ 'scene_codec': source_codec,
324
+ 'scene_container': scene_extension[1:], # Remove the dot
325
+ 'file_size_mb': file_size_mb,
326
+ 'scene_detection_method': 'force_time_based',
327
+ 'original_video_metadata': original_metadata
328
+ }
329
+ scene_metadata_list.append(scene_metadata)
330
+
331
+ if logging_enabled:
332
+ print(f" ✅ Created: {scene_filename} ({file_size_mb:.1f} MB)")
333
+ else:
334
+ if logging_enabled:
335
+ print(f" ❌ Failed: {scene_filename} (file not created or empty)")
336
+ else:
337
+ if logging_enabled:
338
+ print(f" ❌ FFmpeg failed for {scene_filename}: {result.stderr}")
339
+
340
+ except subprocess.TimeoutExpired:
341
+ if logging_enabled:
342
+ print(f" ❌ Timeout creating {scene_filename}")
343
+ except Exception as e:
344
+ if logging_enabled:
345
+ print(f" ❌ Error creating {scene_filename}: {e}")
346
+
347
+ if not scene_metadata_list:
348
+ if logging_enabled:
349
+ print(f" ❌ All segment creation failed, using original video as single scene")
350
+ return [{
351
+ 'path': video_path,
352
+ 'scene_number': 1,
353
+ 'start_time': 0.0,
354
+ 'end_time': total_duration,
355
+ 'duration': total_duration,
356
+ 'original_video_metadata': original_metadata
357
+ }]
358
+
359
+ if logging_enabled:
360
+ print(f" ✅ Successfully created {len(scene_metadata_list)} time-based segments")
361
+
362
+ return scene_metadata_list
363
+
364
+ def time_based_splitting(video_path, temp_dir, config, duration, original_metadata, logging_enabled=True):
365
+ """
366
+ Split video into scenes and return metadata for each scene.
367
+
368
+ Args:
369
+ video_path (str): Path to the video file
370
+ temp_dir (str): Temporary directory for scene files
371
+ config (dict): Configuration dictionary
372
+ duration (float): Video duration in seconds
373
+ original_metadata (dict): Complete metadata from Part 1 including codec info
374
+ logging_enabled (bool): Enable detailed logging
375
+
376
+ Returns:
377
+ list: List of scene metadata dictionaries
378
+ """
379
+ if logging_enabled:
380
+ print(f"\n--- Running time-based scene splitting with metadata ---")
381
+ # ✅ NEW: Log codec information for scene splitting
382
+ print(f" 🎥 Source codec: {original_metadata.get('codec', 'unknown')}")
383
+ print(f" 🎯 Target codec: {original_metadata.get('target_codec', 'auto')}")
384
+
385
+ try:
386
+ total_duration = duration
387
+ if logging_enabled:
388
+ print(f"Using provided duration: {total_duration:.1f}s")
389
+
390
+ if not total_duration:
391
+ return [{
392
+ 'path': video_path,
393
+ 'scene_number': 1,
394
+ 'start_time': 0.0,
395
+ 'end_time': duration,
396
+ 'duration': duration,
397
+ 'original_video_metadata': original_metadata
398
+ }]
399
+
400
+ # Get scene duration from config
401
+ scene_config = config.get('scene_detection', {})
402
+ scene_duration = scene_config.get('time_based_scene_duration', None)
403
+
404
+ # Auto-calculate scene duration if not specified
405
+ if scene_duration is None:
406
+ if total_duration <= 120:
407
+ scene_duration = 60
408
+ elif total_duration <= 600:
409
+ scene_duration = 120
410
+ elif total_duration <= 1800:
411
+ scene_duration = 300
412
+ else:
413
+ scene_duration = 600
414
+
415
+ if logging_enabled:
416
+ print(f"Auto-calculated scene duration: {scene_duration}s for {total_duration:.1f}s video")
417
+
418
+ # Calculate scene boundaries
419
+ scene_boundaries = []
420
+ current_start = 0
421
+
422
+ while current_start < total_duration:
423
+ scene_end = min(current_start + scene_duration, total_duration)
424
+ scene_boundaries.append((current_start, scene_end))
425
+ current_start = scene_end
426
+
427
+ if logging_enabled:
428
+ print(f"Calculated {len(scene_boundaries)} time-based scenes:")
429
+ for i, (start, end) in enumerate(scene_boundaries):
430
+ print(f" Scene {i+1}: {start:.1f}s - {end:.1f}s (duration: {end-start:.1f}s)")
431
+
432
+ # If only one scene, return original video
433
+ if len(scene_boundaries) <= 1:
434
+ return [{
435
+ 'path': video_path,
436
+ 'scene_number': 1,
437
+ 'start_time': 0.0,
438
+ 'end_time': total_duration,
439
+ 'duration': total_duration,
440
+ 'original_video_metadata': original_metadata
441
+ }]
442
+
443
+ # Create output directory
444
+ os.makedirs(temp_dir, exist_ok=True)
445
+
446
+ # ✅ IMPROVED: Choose scene file extension based on source codec
447
+ source_codec = original_metadata.get('codec', 'h264').lower()
448
+ if source_codec in ['ffv1', 'prores', 'dnxhd']:
449
+ scene_extension = '.mkv' # Use MKV for lossless codecs
450
+ elif source_codec in ['av1', 'vp9']:
451
+ scene_extension = '.webm' # Use WebM for modern codecs
452
+ else:
453
+ scene_extension = '.mp4' # Default to MP4 for compatibility
454
+
455
+ if logging_enabled:
456
+ print(f" 📦 Using {scene_extension} container for {source_codec} codec")
457
+
458
+ # Split video and create metadata
459
+ scene_metadata_list = []
460
+
461
+ for i, (start_time_sec, end_time_sec) in enumerate(scene_boundaries):
462
+ scene_number = i + 1
463
+ scene_filename = f"scene_{scene_number:03d}{scene_extension}"
464
+ scene_path = os.path.join(temp_dir, scene_filename)
465
+
466
+ duration_scene = end_time_sec - start_time_sec
467
+
468
+ # ✅ IMPROVED: FFmpeg command with codec-aware copy settings
469
+ cmd = [
470
+ 'ffmpeg', '-y',
471
+ '-ss', str(start_time_sec),
472
+ '-i', video_path,
473
+ '-t', str(duration_scene),
474
+ '-c', 'copy', # Stream copy for speed
475
+ '-map', '0', # Copy all streams
476
+ '-avoid_negative_ts', 'make_zero',
477
+ scene_path
478
+ ]
479
+
480
+ # ✅ NEW: Add codec-specific options if needed
481
+ if source_codec == 'ffv1':
482
+ # For FFV1, ensure proper MKV muxing
483
+ cmd.extend(['-f', 'matroska'])
484
+ elif source_codec in ['av1', 'vp9']:
485
+ # For modern codecs, ensure proper WebM muxing
486
+ cmd.extend(['-f', 'webm'])
487
+
488
+ if logging_enabled:
489
+ print(f"Extracting scene {scene_number}/{len(scene_boundaries)}: {start_time_sec:.1f}s-{end_time_sec:.1f}s")
490
+
491
+ try:
492
+ result = subprocess.run(
493
+ cmd,
494
+ stdout=subprocess.PIPE,
495
+ stderr=subprocess.PIPE,
496
+ text=True,
497
+ check=True
498
+ )
499
+
500
+ if os.path.exists(scene_path) and os.path.getsize(scene_path) > 0:
501
+ # Create scene metadata
502
+ scene_metadata = {
503
+ 'path': scene_path,
504
+ 'scene_number': scene_number,
505
+ 'start_time': start_time_sec,
506
+ 'end_time': end_time_sec,
507
+ 'duration': duration_scene,
508
+ 'original_video_metadata': original_metadata, # ✅ Complete metadata including codec info
509
+ 'file_size_mb': os.path.getsize(scene_path) / (1024 * 1024),
510
+ 'scene_codec': source_codec, # ✅ NEW: Track scene codec
511
+ 'scene_container': scene_extension[1:] # ✅ NEW: Track container format
512
+ }
513
+ scene_metadata_list.append(scene_metadata)
514
+
515
+ if logging_enabled:
516
+ print(f" ✅ Created: {scene_filename} ({scene_metadata['file_size_mb']:.1f} MB)")
517
+ else:
518
+ if logging_enabled:
519
+ print(f" ❌ Failed: {scene_filename}")
520
+
521
+ except subprocess.CalledProcessError as e:
522
+ if logging_enabled:
523
+ print(f" ❌ FFmpeg error for scene {scene_number}: {e.stderr}")
524
+ continue
525
+ except Exception as e:
526
+ if logging_enabled:
527
+ print(f" ❌ Unexpected error for scene {scene_number}: {e}")
528
+ continue
529
+
530
+ if logging_enabled:
531
+ print(f"Time-based splitting completed: {len(scene_metadata_list)} scenes created")
532
+ if scene_metadata_list:
533
+ total_size = sum(scene['file_size_mb'] for scene in scene_metadata_list)
534
+ print(f" 📊 Total scenes size: {total_size:.1f} MB")
535
+
536
+ return scene_metadata_list if scene_metadata_list else [{
537
+ 'path': video_path,
538
+ 'scene_number': 1,
539
+ 'start_time': 0.0,
540
+ 'end_time': total_duration,
541
+ 'duration': total_duration,
542
+ 'original_video_metadata': original_metadata
543
+ }]
544
+
545
+ except Exception as e:
546
+ if logging_enabled:
547
+ print(f"Time-based splitting failed: {e}")
548
+ return [{
549
+ 'path': video_path,
550
+ 'scene_number': 1,
551
+ 'start_time': 0.0,
552
+ 'end_time': duration,
553
+ 'duration': duration,
554
+ 'original_video_metadata': original_metadata
555
+ }]
556
+
557
+ def create_scene_metadata_from_files(scene_files, original_metadata, config):
558
+ """
559
+ Create metadata for scenes created by PySceneDetect.
560
+ Note: PySceneDetect doesn't give us exact timing, so we estimate.
561
+
562
+ Args:
563
+ scene_files (list): List of scene file paths
564
+ original_metadata (dict): Complete metadata from Part 1 including codec info
565
+ config (dict): Configuration dictionary
566
+
567
+ Returns:
568
+ list: List of scene metadata dictionaries
569
+ """
570
+ scene_metadata_list = []
571
+ total_duration = original_metadata['duration']
572
+ source_codec = original_metadata.get('codec', 'unknown')
573
+
574
+ if not scene_files:
575
+ return [{
576
+ 'path': original_metadata['path'],
577
+ 'scene_number': 1,
578
+ 'start_time': 0.0,
579
+ 'end_time': total_duration,
580
+ 'duration': total_duration,
581
+ 'original_video_metadata': original_metadata
582
+ }]
583
+
584
+ # Estimate timing based on file count (not perfect, but reasonable)
585
+ estimated_scene_duration = total_duration / len(scene_files)
586
+
587
+ print(f" 🎥 Creating metadata for {len(scene_files)} scenes from {source_codec} source")
588
+
589
+ for i, scene_file in enumerate(scene_files):
590
+ scene_number = i + 1
591
+ start_time = i * estimated_scene_duration
592
+ end_time = min((i + 1) * estimated_scene_duration, total_duration)
593
+
594
+ # Adjust last scene to cover remaining time
595
+ if i == len(scene_files) - 1:
596
+ end_time = total_duration
597
+
598
+ # ✅ NEW: Extract scene file extension for container tracking
599
+ scene_extension = os.path.splitext(scene_file)[1][1:] if os.path.splitext(scene_file)[1] else 'unknown'
600
+
601
+ scene_metadata = {
602
+ 'path': scene_file,
603
+ 'scene_number': scene_number,
604
+ 'start_time': start_time,
605
+ 'end_time': end_time,
606
+ 'duration': end_time - start_time,
607
+ 'original_video_metadata': original_metadata, # ✅ Complete metadata including codec info
608
+ 'file_size_mb': os.path.getsize(scene_file) / (1024 * 1024) if os.path.exists(scene_file) else 0,
609
+ 'scene_codec': source_codec, # ✅ NEW: Track scene codec
610
+ 'scene_container': scene_extension # ✅ NEW: Track container format
611
+ }
612
+ scene_metadata_list.append(scene_metadata)
613
+
614
+ return scene_metadata_list
services/compress/server.py ADDED
@@ -0,0 +1,798 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Video Compression Service API Server
3
+
4
+ This module provides a FastAPI server for video compression services.
5
+ It handles video upload, compression, and storage operations.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import time
11
+ from datetime import datetime
12
+ from pathlib import Path
13
+ from typing import Optional
14
+
15
+ from fastapi import FastAPI, HTTPException
16
+ from pydantic import BaseModel
17
+ from loguru import logger
18
+
19
+ from video_preprocessor import pre_processing
20
+ from scene_detector import scene_detection
21
+ from encoder import ai_encoding, load_encoding_resources
22
+ from vmaf_calculator import scene_vmaf_calculation
23
+ from validator_merger import validation_and_merging
24
+ from vidaio_subnet_core.utilities import storage_client, download_video
25
+ from vidaio_subnet_core import CONFIG
26
+
27
+
28
+ # ============================================================================
29
+ # FastAPI Application Setup
30
+ # ============================================================================
31
+
32
+ app = FastAPI(title="Video Compression Service", version="1.0.0")
33
+
34
+
35
+ # ============================================================================
36
+ # Data Models
37
+ # ============================================================================
38
+
39
+ class CompressPayload(BaseModel):
40
+ """Payload for video compression requests."""
41
+ payload_url: str
42
+ vmaf_threshold: float
43
+ target_quality: str = 'Medium' # High, Medium, Low
44
+ max_duration: int = 3600 # Maximum allowed video duration in seconds
45
+ output_dir: str = './output' # Output directory for final files
46
+
47
+
48
+ class TestCompressPayload(BaseModel):
49
+ """Payload for test compression requests."""
50
+ video_path: str
51
+
52
+
53
+ # ============================================================================
54
+ # API Endpoints
55
+ # ============================================================================
56
+
57
+ @app.post("/compress-video")
58
+ async def compress_video(video: CompressPayload):
59
+ """
60
+ Compress a video from a URL payload.
61
+
62
+ Args:
63
+ video: Compression request payload
64
+
65
+ Returns:
66
+ dict: Compression results with uploaded video URL
67
+ """
68
+ print(f"video url: {video.payload_url}")
69
+ print(f"vmaf threshold: {video.vmaf_threshold}")
70
+
71
+ # Download video from URL
72
+ input_path = await download_video(video.payload_url)
73
+ input_file = Path(input_path)
74
+ vmaf_threshold = video.vmaf_threshold
75
+
76
+ # Map VMAF threshold to target quality
77
+ if vmaf_threshold == 85:
78
+ target_quality = 'Low'
79
+ elif vmaf_threshold == 90:
80
+ target_quality = 'Medium'
81
+ elif vmaf_threshold == 95:
82
+ target_quality = 'High'
83
+ else:
84
+ raise HTTPException(status_code=400, detail="Invalid VMAF threshold.")
85
+
86
+ # Validate input file
87
+ if not input_file.is_file():
88
+ raise HTTPException(status_code=400, detail="Input video file does not exist.")
89
+
90
+ # Create output directory
91
+ output_dir = Path(video.output_dir)
92
+ output_dir.mkdir(parents=True, exist_ok=True)
93
+
94
+ # Perform video compression
95
+ try:
96
+ compressed_video_path = video_compressor(
97
+ input_file=str(input_file),
98
+ target_quality=target_quality,
99
+ max_duration=video.max_duration,
100
+ output_dir=str(output_dir)
101
+ )
102
+ print(f"compressed_video_path: {compressed_video_path}")
103
+
104
+ if compressed_video_path and Path(compressed_video_path).exists():
105
+ # Upload compressed video to storage
106
+ try:
107
+ compressed_video_name = os.path.basename(compressed_video_path)
108
+ object_name: str = compressed_video_name
109
+
110
+ # Upload file
111
+ await storage_client.upload_file(object_name, compressed_video_path)
112
+ print(f"object_name: {object_name}")
113
+ print("Video uploaded successfully.")
114
+
115
+ # Clean up local file
116
+ if os.path.exists(compressed_video_path):
117
+ os.remove(compressed_video_path)
118
+ print(f"{compressed_video_path} has been deleted.")
119
+ else:
120
+ print(f"{compressed_video_path} does not exist.")
121
+
122
+ # Get sharing link
123
+ sharing_link: Optional[str] = await storage_client.get_presigned_url(object_name)
124
+ print(f"sharing_link: {sharing_link}")
125
+
126
+ if not sharing_link:
127
+ print("Upload failed")
128
+ return {"uploaded_video_url": None}
129
+
130
+ return {
131
+ "uploaded_video_url": sharing_link,
132
+ "status": "success",
133
+ "compressed_video_path": str(compressed_video_path)
134
+ }
135
+ except Exception as upload_error:
136
+ raise HTTPException(
137
+ status_code=500,
138
+ detail=f"Failed to upload compressed video: {str(upload_error)}"
139
+ )
140
+ else:
141
+ raise HTTPException(status_code=500, detail="Video compression failed")
142
+ except Exception as e:
143
+ raise HTTPException(status_code=500, detail=f"Video compression error: {str(e)}")
144
+
145
+
146
+ @app.post("/test-compress")
147
+ async def test_compress_video(test_payload: TestCompressPayload):
148
+ """
149
+ Test endpoint for video compression using local video path.
150
+
151
+ Args:
152
+ test_payload: Test compression request payload
153
+
154
+ Returns:
155
+ dict: Test compression results
156
+ """
157
+ video_path = Path(test_payload.video_path)
158
+
159
+ # Validate input file
160
+ if not video_path.is_file():
161
+ raise HTTPException(
162
+ status_code=400,
163
+ detail=f"Video file does not exist: {video_path}"
164
+ )
165
+
166
+ try:
167
+ # Perform test compression
168
+ compressed_video_path = test_video_compression(str(video_path))
169
+
170
+ if compressed_video_path and Path(compressed_video_path).exists():
171
+ return {
172
+ "status": "success",
173
+ "message": "Video compression test completed successfully",
174
+ "input_path": str(video_path),
175
+ "output_path": compressed_video_path,
176
+ "output_size_mb": round(
177
+ Path(compressed_video_path).stat().st_size / (1024 * 1024), 2
178
+ )
179
+ }
180
+ else:
181
+ raise HTTPException(status_code=500, detail="Video compression test failed")
182
+
183
+ except Exception as e:
184
+ raise HTTPException(
185
+ status_code=500,
186
+ detail=f"Video compression test error: {str(e)}"
187
+ )
188
+
189
+
190
+ # ============================================================================
191
+ # Core Video Compression Functions
192
+ # ============================================================================
193
+
194
+ def video_compressor(
195
+ input_file: str,
196
+ target_quality: str = 'Medium',
197
+ max_duration: int = 3600,
198
+ output_dir: str = './output'
199
+ ) -> Optional[str]:
200
+ """
201
+ Main video compression pipeline orchestrator.
202
+
203
+ Args:
204
+ input_file: Path to input video file
205
+ target_quality: Target quality level ('High', 'Medium', 'Low')
206
+ max_duration: Maximum allowed video duration in seconds
207
+ output_dir: Output directory for final files
208
+
209
+ Returns:
210
+ str: Path to compressed video file, or None if failed
211
+ """
212
+ # Record pipeline start time
213
+ pipeline_start_time = time.time()
214
+
215
+ # Get current directory and setup paths
216
+ current_dir = os.path.dirname(os.path.abspath(__file__))
217
+ output_dir_path = Path(output_dir)
218
+ output_dir_path.mkdir(parents=True, exist_ok=True)
219
+
220
+ # Load configuration
221
+ config = _load_configuration(current_dir)
222
+ config['directories']['output_dir'] = str(output_dir_path)
223
+ if 'video_processing' not in config:
224
+ config['video_processing'] = {}
225
+ config['video_processing']['target_quality'] = target_quality
226
+
227
+ # Create temp directory
228
+ temp_dir = Path(config['directories']['temp_dir'])
229
+ temp_dir.mkdir(parents=True, exist_ok=True)
230
+
231
+ # Display pipeline information
232
+ _display_pipeline_info(input_file, target_quality, max_duration, output_dir)
233
+
234
+ # PART 1: Pre-processing
235
+ part1_result = _execute_preprocessing(input_file, target_quality, max_duration, output_dir_path)
236
+ if not part1_result:
237
+ print("❌ Part 1 failed. Pipeline terminated.")
238
+ return False
239
+
240
+ part1_time = time.time() - pipeline_start_time
241
+ _display_preprocessing_results(part1_result, part1_time)
242
+
243
+ # PART 2: Scene Detection
244
+ part2_start_time = time.time()
245
+ scenes_metadata = scene_detection(part1_result)
246
+ if not scenes_metadata:
247
+ print("❌ Part 2 failed. Pipeline terminated.")
248
+ return False
249
+
250
+ part2_time = time.time() - part2_start_time
251
+ _display_scene_detection_results(scenes_metadata, part2_time)
252
+
253
+ # PART 3: AI Encoding
254
+ part3_result = _execute_ai_encoding(scenes_metadata, config, target_quality)
255
+ if not part3_result:
256
+ print("❌ Part 3 failed completely. Pipeline terminated.")
257
+ return False
258
+
259
+ part3_time = part3_result['processing_time']
260
+ encoded_scenes_data = part3_result['encoded_scenes_data']
261
+ successful_encodings = part3_result['successful_encodings']
262
+
263
+ # PART 4: Validation and Merging
264
+ part4_result = _execute_validation_and_merging(
265
+ part1_result, encoded_scenes_data, config
266
+ )
267
+ if not part4_result:
268
+ print("❌ Part 4 failed. Could not create final video.")
269
+ return False
270
+
271
+ part4_time = part4_result['processing_time']
272
+ final_video_path = part4_result['final_video_path']
273
+ final_vmaf = part4_result['final_vmaf']
274
+ comprehensive_report = part4_result['comprehensive_report']
275
+
276
+ _display_validation_results(part4_result)
277
+
278
+ # Pipeline completion summary
279
+ total_pipeline_time = time.time() - pipeline_start_time
280
+ _display_pipeline_summary(
281
+ input_file, final_video_path, part1_result, scenes_metadata,
282
+ successful_encodings, output_dir, pipeline_start_time,
283
+ part1_time, part2_time, part3_time, part4_time, total_pipeline_time,
284
+ final_vmaf, comprehensive_report
285
+ )
286
+
287
+ return final_video_path
288
+
289
+
290
+ def test_video_compression(video_path: str) -> Optional[str]:
291
+ """
292
+ Test function for video compression using default parameters.
293
+
294
+ Args:
295
+ video_path: Path to input video file
296
+
297
+ Returns:
298
+ str: Path to compressed video file, or None if failed
299
+ """
300
+ print(f"\n🧪 === Testing Video Compression ===")
301
+ print(f" 📁 Input: {video_path}")
302
+ print(f" 🎯 Using default test parameters")
303
+
304
+ # Default test parameters
305
+ test_params = {
306
+ 'target_quality': 'Medium',
307
+ 'max_duration': 3600,
308
+ 'output_dir': './test_output'
309
+ }
310
+
311
+ try:
312
+ # Validate input file
313
+ input_path = Path(video_path)
314
+ if not input_path.is_file():
315
+ print(f"❌ Input file does not exist: {video_path}")
316
+ return None
317
+ # Create test output directory
318
+ test_output_dir = Path(test_params['output_dir'])
319
+ test_output_dir.mkdir(parents=True, exist_ok=True)
320
+
321
+ # Perform compression
322
+ result = video_compressor(
323
+ input_file=str(input_path),
324
+ target_quality=test_params['target_quality'],
325
+ max_duration=test_params['max_duration'],
326
+ output_dir=str(test_output_dir)
327
+ )
328
+
329
+ if result and Path(result).exists():
330
+ print(f"\n✅ Test completed successfully!")
331
+ print(f" 📁 Compressed video: {result}")
332
+ return result
333
+ else:
334
+ print(f"\n❌ Test failed - no output file generated")
335
+ return None
336
+
337
+ except Exception as e:
338
+ print(f"\n❌ Test failed with exception: {e}")
339
+ return None
340
+
341
+
342
+ # ============================================================================
343
+ # Helper Functions
344
+ # ============================================================================
345
+
346
+ def _load_configuration(current_dir: str) -> dict:
347
+ """Load configuration from config.json or use defaults."""
348
+ try:
349
+ config_path = os.path.join(current_dir, 'config.json')
350
+ with open(config_path, 'r') as f:
351
+ config = json.load(f)
352
+ print("✅ Configuration loaded successfully")
353
+ return config
354
+ except FileNotFoundError:
355
+ print("⚠️ Config file not found, using default configuration")
356
+ return _get_default_config()
357
+
358
+
359
+ def _get_default_config() -> dict:
360
+ """Get default configuration when config.json is not available."""
361
+ return {
362
+ 'directories': {
363
+ 'temp_dir': './videos/temp_scenes',
364
+ 'output_dir': './output'
365
+ },
366
+ 'video_processing': {
367
+ 'SHORT_VIDEO_THRESHOLD': 20,
368
+ 'target_vmaf': 93.0,
369
+ 'codec': 'auto',
370
+ 'size_increase_protection': True,
371
+ 'conservative_cq_adjustment': 2,
372
+ 'max_output_size_ratio': 1.15,
373
+ 'max_encoding_retries': 2,
374
+ 'basic_cq_lookup_by_quality': {
375
+ 'High': {
376
+ 'animation': 22,
377
+ 'low-action': 20,
378
+ 'medium-action': 18,
379
+ 'high-action': 16,
380
+ 'default': 19
381
+ },
382
+ 'Medium': {
383
+ 'animation': 25,
384
+ 'low-action': 23,
385
+ 'medium-action': 21,
386
+ 'high-action': 19,
387
+ 'default': 22
388
+ },
389
+ 'Low': {
390
+ 'animation': 28,
391
+ 'low-action': 26,
392
+ 'medium-action': 24,
393
+ 'high-action': 22,
394
+ 'default': 25
395
+ }
396
+ },
397
+ },
398
+ 'scene_detection': {
399
+ 'enable_time_based_fallback': True,
400
+ 'time_based_scene_duration': 90
401
+ },
402
+ 'vmaf_calculation': {
403
+ 'calculate_full_video_vmaf': True,
404
+ 'vmaf_use_sampling': True,
405
+ 'vmaf_num_clips': 3,
406
+ 'vmaf_clip_duration': 2
407
+ },
408
+ 'output_settings': {
409
+ 'save_individual_scene_reports': True,
410
+ 'save_comprehensive_report': True
411
+ },
412
+ 'model_paths': {
413
+ 'scene_classifier_model': 'services/compress/models/scene_classifier_model.pth'
414
+ }
415
+ }
416
+
417
+
418
+ def _display_pipeline_info(input_file: str, target_quality: str, max_duration: int, output_dir: str):
419
+ """Display pipeline initialization information."""
420
+ print(f"\n🎬 === AI Video Compression Pipeline ===")
421
+ print(f" 📁 Input: {Path(input_file).name}")
422
+ print(f" 🎯 Target Quality: {target_quality}")
423
+ print(f" ⏱️ Max Duration: {max_duration}s")
424
+ print(f" 📁 Output Dir: {output_dir}")
425
+ print(f" 🕐 Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
426
+
427
+
428
+ def _execute_preprocessing(input_file: str, target_quality: str, max_duration: int, output_dir_path: Path) -> Optional[dict]:
429
+ """Execute Part 1: Pre-processing."""
430
+ print(f"\n🔧 === Part 1: Pre-processing ===")
431
+ part1_start_time = time.time()
432
+
433
+ part1_result = pre_processing(
434
+ video_path=input_file,
435
+ target_quality=target_quality,
436
+ max_duration=max_duration,
437
+ output_dir=output_dir_path,
438
+ codec='libsvtav1'
439
+ )
440
+
441
+ print("part1_result", part1_result)
442
+ return part1_result
443
+
444
+
445
+ def _display_preprocessing_results(part1_result: dict, part1_time: float):
446
+ """Display Part 1 results."""
447
+ print(f"\n✅ Part 1 completed in {part1_time:.1f}s:")
448
+ print(f" 📁 Video: {os.path.basename(part1_result['path'])}")
449
+ print(f" 🎥 Codec: {part1_result['codec']} (original: {part1_result['original_codec']})")
450
+ print(f" ⏱️ Duration: {part1_result['duration']:.1f}s")
451
+ print(f" 🔄 Reencoded: {part1_result['was_reencoded']}")
452
+ print(f" 🎯 Target VMAF: {part1_result['target_vmaf']} ({part1_result['target_quality']})")
453
+
454
+ if part1_result['was_reencoded']:
455
+ print(f" 🔄 Lossless conversion: {part1_result['processing_info']['original_format']} → {part1_result['processing_info']['standardized_format']}")
456
+ print(f" ⏱️ Encoding time: {part1_result['encoding_time']:.1f}s")
457
+
458
+
459
+ def _display_scene_detection_results(scenes_metadata: list, part2_time: float):
460
+ """Display Part 2 results."""
461
+ print(f"\n✅ Part 2 completed in {part2_time:.1f}s: {len(scenes_metadata)} scenes detected")
462
+
463
+ # Display scene information
464
+ total_scene_size = 0
465
+ for scene in scenes_metadata:
466
+ scene_size = scene.get('file_size_mb', 0)
467
+ total_scene_size += scene_size
468
+ print(f" Scene {scene['scene_number']}: {scene['start_time']:.1f}s - {scene['end_time']:.1f}s "
469
+ f"(duration: {scene['duration']:.1f}s)")
470
+ if scene_size > 0:
471
+ print(f" 📁 File: {os.path.basename(scene['path'])} ({scene_size:.1f} MB)")
472
+ else:
473
+ print(f" 📁 File: {os.path.basename(scene['path'])}")
474
+
475
+ if total_scene_size > 0:
476
+ print(f" 📊 Total scene files: {total_scene_size:.1f} MB")
477
+
478
+
479
+ def _execute_ai_encoding(scenes_metadata: list, config: dict, target_quality: str) -> Optional[dict]:
480
+ """Execute Part 3: AI Encoding."""
481
+ print(f"\n🧠 === Part 3: AI Encoding ===")
482
+ part3_start_time = time.time()
483
+
484
+ print(f" 🔧 Loading AI models and resources...")
485
+ print(f" 📋 Using quality-based CQ lookup tables for {target_quality} quality")
486
+ print(f" 🎯 Target Quality Level: {target_quality}")
487
+
488
+ # Display CQ ranges for selected quality level
489
+ quality_info = {
490
+ 'High': {'vmaf': 95, 'cq_range': '16-22'},
491
+ 'Medium': {'vmaf': 93, 'cq_range': '19-25'},
492
+ 'Low': {'vmaf': 90, 'cq_range': '22-28'}
493
+ }
494
+
495
+ if target_quality in quality_info:
496
+ info = quality_info[target_quality]
497
+ print(f" 🎚️ CQ Range for {target_quality}: {info['cq_range']} (Target VMAF: {info['vmaf']})")
498
+
499
+ print(f" 🔧 Loading AI models and resources...")
500
+
501
+ try:
502
+ resources = load_encoding_resources(config, logging_enabled=True)
503
+ print(f" ✅ AI resources loaded successfully")
504
+ print(f" 🧠 Mode: Scene classification + CQ lookup table")
505
+ except Exception as e:
506
+ print(f" ❌ Failed to load GGG AI resources: {e}")
507
+ return None
508
+
509
+ # Process each scene individually
510
+ encoded_scenes_data = []
511
+ successful_encodings = 0
512
+ failed_encodings = 0
513
+ total_input_size = 0
514
+ total_output_size = 0
515
+
516
+ print(f"\n 📊 Processing {len(scenes_metadata)} scenes with AI approach...")
517
+
518
+ for i, scene_metadata in enumerate(scenes_metadata):
519
+ scene_result = _process_single_scene(
520
+ scene_metadata, i, len(scenes_metadata), config, resources, target_quality
521
+ )
522
+
523
+ if scene_result['success']:
524
+ successful_encodings += 1
525
+ total_input_size += scene_result['input_size_mb']
526
+ total_output_size += scene_result['output_size_mb']
527
+ else:
528
+ failed_encodings += 1
529
+
530
+ encoded_scenes_data.append(scene_result['scene_data'])
531
+
532
+ part3_time = time.time() - part3_start_time
533
+
534
+ # Display Part 3 summary
535
+ _display_ai_encoding_summary(
536
+ successful_encodings, failed_encodings, len(scenes_metadata),
537
+ part3_time, target_quality, total_input_size, total_output_size
538
+ )
539
+
540
+ if successful_encodings == 0:
541
+ print("❌ Part 3 failed completely. No scenes were encoded. Pipeline terminated.")
542
+ return None
543
+
544
+ return {
545
+ 'encoded_scenes_data': encoded_scenes_data,
546
+ 'successful_encodings': successful_encodings,
547
+ 'failed_encodings': failed_encodings,
548
+ 'processing_time': part3_time,
549
+ 'total_input_size': total_input_size,
550
+ 'total_output_size': total_output_size
551
+ }
552
+
553
+
554
+ def _process_single_scene(scene_metadata: dict, scene_index: int, total_scenes: int,
555
+ config: dict, resources: dict, target_quality: str) -> dict:
556
+ """Process a single scene for AI encoding."""
557
+ scene_number = scene_metadata['scene_number']
558
+ scene_path = scene_metadata['path']
559
+ scene_duration = scene_metadata['duration']
560
+
561
+ print(f"\n 🎬 Scene {scene_number}/{total_scenes}: {os.path.basename(scene_path)}")
562
+ print(f" ⏱️ Duration: {scene_duration:.1f}s")
563
+
564
+ indicative_vmaf = scene_metadata['original_video_metadata'].get('target_vmaf')
565
+ print(f" 🎯 Target Quality: {target_quality}" + (f" (VMAF≈{indicative_vmaf})" if indicative_vmaf else ""))
566
+ print(f" 🧠 Method: scene classification + CQ lookup")
567
+
568
+ scene_start_time = time.time()
569
+
570
+ try:
571
+ encoded_path, scene_data = ai_encoding(
572
+ scene_metadata=scene_metadata,
573
+ config=config,
574
+ resources=resources,
575
+ target_vmaf=None,
576
+ target_quality_level=target_quality,
577
+ logging_enabled=True
578
+ )
579
+
580
+ scene_processing_time = time.time() - scene_start_time
581
+
582
+ if encoded_path and scene_data.get('encoding_success', False):
583
+ size_mb = scene_data.get('encoded_file_size_mb', 0)
584
+ input_size_mb = scene_data.get('input_size_mb', 0)
585
+ compression = scene_data.get('compression_ratio', 0)
586
+
587
+ print(f" ✅ Scene {scene_number} encoded successfully")
588
+ print(f" 📁 Output: {os.path.basename(encoded_path)}")
589
+ print(f" 📊 Size: {input_size_mb:.1f} MB → {size_mb:.1f} MB ({compression:+.1f}% compression)")
590
+ print(f" 🎭 Scene type: {scene_data.get('scene_type', 'unknown')}")
591
+ print(f" 🎯 Quality: {scene_data.get('target_quality_level', target_quality)}")
592
+ print(f" 🎚️ CQ used: {scene_data.get('base_cq_for_quality', 'N/A')} → {scene_data.get('final_adjusted_cq', 'unknown')} (after adjustment)")
593
+ print(f" 📋 Method: Quality-based lookup table CQ selection")
594
+ print(f" ⏱️ Processing: {scene_processing_time:.1f}s")
595
+
596
+ # Update scene metadata
597
+ scene_metadata['encoded_path'] = encoded_path
598
+ scene_metadata['encoding_data'] = scene_data
599
+
600
+ return {
601
+ 'success': True,
602
+ 'scene_data': scene_data,
603
+ 'input_size_mb': input_size_mb,
604
+ 'output_size_mb': size_mb
605
+ }
606
+ else:
607
+ error_reason = scene_data.get('error_reason', 'Unknown error')
608
+ print(f" ❌ Scene {scene_number} encoding failed: {error_reason}")
609
+ print(f" ⏱️ Processing: {scene_processing_time:.1f}s")
610
+
611
+ scene_metadata['encoded_path'] = None
612
+ scene_metadata['encoding_data'] = scene_data
613
+
614
+ return {
615
+ 'success': False,
616
+ 'scene_data': scene_data,
617
+ 'input_size_mb': 0,
618
+ 'output_size_mb': 0
619
+ }
620
+
621
+ except Exception as e:
622
+ scene_processing_time = time.time() - scene_start_time
623
+ print(f" ❌ Scene {scene_number} processing failed with exception: {e}")
624
+ print(f" ⏱️ Processing: {scene_processing_time:.1f}s")
625
+
626
+ error_scene_data = {
627
+ 'scene_number': scene_number,
628
+ 'encoding_success': False,
629
+ 'error_reason': f'Exception: {str(e)}',
630
+ 'processing_time_seconds': scene_processing_time,
631
+ 'encoded_path': None,
632
+ 'original_video_metadata': scene_metadata['original_video_metadata']
633
+ }
634
+
635
+ scene_metadata['encoded_path'] = None
636
+ scene_metadata['encoding_data'] = error_scene_data
637
+
638
+ return {
639
+ 'success': False,
640
+ 'scene_data': error_scene_data,
641
+ 'input_size_mb': 0,
642
+ 'output_size_mb': 0
643
+ }
644
+
645
+
646
+ def _display_ai_encoding_summary(successful_encodings: int, failed_encodings: int, total_scenes: int,
647
+ part3_time: float, target_quality: str, total_input_size: float, total_output_size: float):
648
+ """Display Part 3 summary."""
649
+ print(f"\n 📊 Part 3 Processing Summary:")
650
+ print(f" ✅ Successful encodings: {successful_encodings}")
651
+ print(f" ❌ Failed encodings: {failed_encodings}")
652
+ print(f" 📈 Success rate: {successful_encodings/total_scenes*100:.1f}%")
653
+ print(f" ⏱️ Total processing time: {part3_time:.1f}s")
654
+ print(f" 🎯 Quality Level: {target_quality}")
655
+ print(f" 🧠 AI Method: Scene classification + quality-based CQ lookup")
656
+
657
+ if total_input_size > 0 and total_output_size > 0:
658
+ overall_compression = (1 - total_output_size / total_input_size) * 100
659
+ print(f" 🗜️ Overall compression: {overall_compression:+.1f}%")
660
+ print(f" 📊 Total size: {total_input_size:.1f} MB → {total_output_size:.1f} MB")
661
+
662
+ print(f"✅ Part 3 completed with {successful_encodings} successful encodings")
663
+
664
+
665
+ def _execute_validation_and_merging(part1_result: dict, encoded_scenes_data: list, config: dict) -> Optional[dict]:
666
+ """Execute Part 4: Validation and Merging."""
667
+ part4_start_time = time.time()
668
+
669
+ try:
670
+ final_video_path, final_vmaf, comprehensive_report = validation_and_merging(
671
+ original_video_path=part1_result['path'],
672
+ encoded_scenes_data=encoded_scenes_data,
673
+ config=config,
674
+ logging_enabled=True
675
+ )
676
+
677
+ part4_time = time.time() - part4_start_time
678
+
679
+ if final_video_path and os.path.exists(final_video_path):
680
+ return {
681
+ 'final_video_path': final_video_path,
682
+ 'final_vmaf': final_vmaf,
683
+ 'comprehensive_report': comprehensive_report,
684
+ 'processing_time': part4_time
685
+ }
686
+ else:
687
+ print("❌ Part 4 failed. Could not create final video.")
688
+ return None
689
+
690
+ except Exception as e:
691
+ print(f"❌ Part 4 failed with exception: {e}")
692
+ return None
693
+
694
+
695
+ def _display_validation_results(part4_result: dict):
696
+ """Display Part 4 results."""
697
+ final_video_path = part4_result['final_video_path']
698
+ final_vmaf = part4_result['final_vmaf']
699
+ comprehensive_report = part4_result['comprehensive_report']
700
+ part4_time = part4_result['processing_time']
701
+
702
+ print(f"✅ Part 4 completed successfully in {part4_time:.1f}s!")
703
+ print(f" 📁 Final video: {os.path.basename(final_video_path)}")
704
+
705
+ if final_vmaf:
706
+ print(f" 🎯 Final VMAF: {final_vmaf:.2f}")
707
+
708
+ if comprehensive_report:
709
+ compression_info = comprehensive_report.get('compression_metrics', {})
710
+ final_compression = compression_info.get('overall_compression_ratio_percent', 0)
711
+ final_size = compression_info.get('final_file_size_mb', 0)
712
+
713
+ print(f" 🗜️ Overall compression: {final_compression:+.1f}%")
714
+ print(f" 📊 Final file size: {final_size:.1f} MB")
715
+
716
+
717
+ def _display_pipeline_summary(input_file: str, final_video_path: str, part1_result: dict,
718
+ scenes_metadata: list, successful_encodings: int, output_dir: str,
719
+ pipeline_start_time: float, part1_time: float, part2_time: float,
720
+ part3_time: float, part4_time: float, total_pipeline_time: float,
721
+ final_vmaf: Optional[float], comprehensive_report: Optional[dict]):
722
+ """Display complete pipeline summary."""
723
+ print(f"\n🎉 === Pipeline Completed Successfully ===")
724
+ print(f" 📁 Input video: {os.path.basename(input_file)}")
725
+ print(f" 📁 Final video: {os.path.basename(final_video_path)}")
726
+ print(f" 🎯 Target quality: {part1_result['target_quality']} (VMAF: {part1_result['target_vmaf']})")
727
+ print(f" 📊 Scenes processed: {len(scenes_metadata)} total, {successful_encodings} successful")
728
+ print(f" 📁 Output directory: {output_dir}")
729
+ print(f" 🕐 Completed: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
730
+
731
+ # Performance breakdown
732
+ print(f"\n ⏱️ Performance Breakdown:")
733
+ print(f" Part 1 (Pre-processing): {part1_time:.1f}s")
734
+ print(f" Part 2 (Scene Detection): {part2_time:.1f}s")
735
+ print(f" Part 3 (AI Encoding): {part3_time:.1f}s")
736
+ print(f" Part 4 (Validation & Merging): {part4_time:.1f}s")
737
+ print(f" Total Pipeline Time: {total_pipeline_time:.1f}s")
738
+
739
+ # Final file size comparison
740
+ input_file_path = Path(input_file)
741
+ final_video_path_obj = Path(final_video_path)
742
+ if input_file_path.exists() and final_video_path_obj.exists():
743
+ input_size = input_file_path.stat().st_size / (1024 * 1024)
744
+ output_size = final_video_path_obj.stat().st_size / (1024 * 1024)
745
+ final_compression = (1 - output_size / input_size) * 100
746
+
747
+ print(f"\n 📊 Final Size Comparison:")
748
+ print(f" Input: {input_size:.1f} MB")
749
+ print(f" Output: {output_size:.1f} MB")
750
+ print(f" Compression: {final_compression:+.1f}%")
751
+
752
+ if final_compression > 0:
753
+ print(f" 💾 Space saved: {input_size - output_size:.1f} MB")
754
+
755
+ # Quality achievement summary
756
+ if final_vmaf and comprehensive_report:
757
+ quality_info = comprehensive_report.get('quality_metrics', {})
758
+ scenes_meeting_target = quality_info.get('scenes_meeting_target', 0)
759
+ avg_scene_vmaf = quality_info.get('average_scene_vmaf', 0)
760
+
761
+ print(f"\n 🎯 Quality Achievement:")
762
+ print(f" Final VMAF: {final_vmaf:.2f}")
763
+ print(f" Average Scene VMAF: {avg_scene_vmaf:.2f}")
764
+ print(f" Scenes meeting target: {scenes_meeting_target}/{len(scenes_metadata)}")
765
+
766
+ if 'prediction_accuracy_stats' in comprehensive_report.get('scene_analysis', {}):
767
+ pred_stats = comprehensive_report['scene_analysis']['prediction_accuracy_stats']
768
+ avg_error = pred_stats.get('average_prediction_error')
769
+ if avg_error:
770
+ print(f" AI prediction accuracy: ±{avg_error:.1f} VMAF points")
771
+
772
+ # Report file locations
773
+ if comprehensive_report:
774
+ print(f"\n 📄 Reports Generated:")
775
+ print(f" 📁 Output directory: {output_dir}")
776
+ print(f" 📊 Comprehensive report: comprehensive_processing_report_*.json")
777
+ print(f" 📄 Individual scene reports: scene_reports/scene_*_report.json")
778
+
779
+ print(f"\n 🎉 Pipeline completed successfully!")
780
+ print(f" 🚀 Ready for playback: {final_video_path}")
781
+
782
+
783
+ # ============================================================================
784
+ # Main Execution
785
+ # ============================================================================
786
+
787
+ if __name__ == "__main__":
788
+ # import uvicorn
789
+
790
+ # logger.info("Starting video compressor server")
791
+ # logger.info(f"Video compressor server running on http://{CONFIG.video_compressor.host}:{CONFIG.video_compressor.port}")
792
+
793
+ # uvicorn.run(app, host=CONFIG.video_compressor.host, port=CONFIG.video_compressor.port)
794
+
795
+ result = test_video_compression('/home/85/test_video/0cb873d6-4c3a-4ced-84e9-87939b08565d.mp4')
796
+ print(result)
797
+
798
+ #python services/compress/server.py
services/compress/utils/__init__.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .video_utils import (
2
+ get_video_duration,
3
+ )
4
+
5
+ from .processing_utils import (
6
+ analyze_input_compression,
7
+ encode_scene_with_size_check,
8
+ should_skip_encoding
9
+ )
10
+ from .fast_scene_detect import adaptive_scene_detection_check, fast_scene_detection_check, motion_based_scene_check
11
+
12
+ # Import preprocessing classes to make them available for pickle loading
13
+ from .data_preprocessing import (
14
+ ColumnDropper, VMAFScaler, TargetExtractor, CQScaler,
15
+ ResolutionTransformer, FeatureScaler, FrameRateTransformer
16
+ )
17
+
18
+ __all__ = [
19
+ 'monitor_memory_usage', 'ProgressTracker',
20
+ 'calculate_perceptual_contrast', 'get_video_duration',
21
+ 'calculate_contrast_adjusted_cq', 'sort_scene_files_by_number',
22
+ 'process_scene_analysis_and_cq',
23
+ 'calculate_vmaf_for_scenes_df',
24
+ 'analyze_input_compression',
25
+ 'calculate_bitrate_aware_cq',
26
+ 'encode_scene_with_size_check',
27
+ 'should_skip_encoding',
28
+ 'create_summary_and_save', 'cleanup_temporary_items',
29
+ 'adaptive_scene_detection_check', 'fast_scene_detection_check', 'motion_based_scene_check',
30
+ 'VideoProcessingLogger',
31
+ # Add preprocessing classes to make them available
32
+ 'ColumnDropper', 'VMAFScaler', 'TargetExtractor', 'CQScaler',
33
+ 'ResolutionTransformer', 'FeatureScaler', 'FrameRateTransformer'
34
+ ]
services/compress/utils/analyze_video_fast.py ADDED
@@ -0,0 +1,568 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import numpy as np
4
+ import cv2
5
+ import subprocess
6
+
7
+ def analyze_video_fast(video_path, max_frames=150,logging_enabled=True,include_quality_metrics=False):
8
+ """
9
+ Enhanced video analysis with ONLY required features for efficient processing.
10
+
11
+ Args:
12
+ video_path: Path to video file
13
+ max_frames: Maximum frames to analyze
14
+ logging_enabled: Whether to log progress
15
+ """
16
+ if not os.path.exists(video_path):
17
+ if logging_enabled:
18
+ print(f"❌ Video file not found: {video_path}")
19
+ return None
20
+
21
+ features = {}
22
+ try:
23
+ # Step 1: Get basic video properties using ffprobe
24
+ basic_features = extract_basic_video_properties(video_path, logging_enabled)
25
+ if not basic_features:
26
+ return None
27
+
28
+ features.update(basic_features)
29
+
30
+ # Step 2: Extract comprehensive motion and complexity metrics using OpenCV
31
+ advanced_features = extract_comprehensive_video_metrics(video_path, max_frames, logging_enabled)
32
+ features.update(advanced_features)
33
+
34
+ # Step 3: Add quality metrics for preprocessing if requested
35
+ if include_quality_metrics:
36
+ if logging_enabled:
37
+ print(" 🔍 Analyzing video quality for preprocessing recommendations...")
38
+ quality_metrics = analyze_video_quality_metrics(video_path, num_frames=20)
39
+ if quality_metrics:
40
+ # Add quality metrics with prefix to avoid conflicts
41
+ for key, value in quality_metrics.items():
42
+ features[f'quality_{key}'] = value
43
+
44
+ if logging_enabled:
45
+ print(f"✅ Video analysis completed:")
46
+
47
+ return features
48
+
49
+ except Exception as e:
50
+ if logging_enabled:
51
+ print(f"❌ Video analysis failed: {e}")
52
+ return None
53
+
54
+ def extract_basic_video_properties(video_path, logging_enabled=True):
55
+ """Extract basic video properties using ffprobe."""
56
+ try:
57
+ ffprobe_cmd = [
58
+ 'ffprobe', '-v', 'quiet', '-print_format', 'json',
59
+ '-show_format', '-show_streams', video_path
60
+ ]
61
+
62
+ result = subprocess.run(ffprobe_cmd, capture_output=True, text=True, timeout=30)
63
+
64
+ if result.returncode != 0:
65
+ if logging_enabled:
66
+ print(f"❌ ffprobe failed: {result.stderr}")
67
+ return None
68
+
69
+ probe_data = json.loads(result.stdout)
70
+
71
+ video_stream = None
72
+ for stream in probe_data.get('streams', []):
73
+ if stream.get('codec_type') == 'video':
74
+ video_stream = stream
75
+ break
76
+
77
+ if not video_stream:
78
+ if logging_enabled:
79
+ print("❌ No video stream found")
80
+ return None
81
+
82
+ features = {}
83
+
84
+ features['metrics_resolution_width'] = int(video_stream.get('width', 0))
85
+ features['metrics_resolution_height'] = int(video_stream.get('height', 0))
86
+
87
+ frame_rate_str = video_stream.get('r_frame_rate', '0/1')
88
+ if '/' in frame_rate_str:
89
+ num, den = frame_rate_str.split('/')
90
+ features['metrics_frame_rate'] = float(num) / float(den) if float(den) != 0 else 0
91
+ else:
92
+ features['metrics_frame_rate'] = float(frame_rate_str)
93
+
94
+ features['metrics_bit_depth'] = int(video_stream.get('bits_per_raw_sample', 8))
95
+
96
+ features['input_codec'] = video_stream.get('codec_name', 'unknown')
97
+
98
+ format_info = probe_data.get('format', {})
99
+ bitrate_bps = 0
100
+
101
+ if video_stream.get('bit_rate'):
102
+ bitrate_bps = int(video_stream['bit_rate'])
103
+ elif format_info.get('bit_rate'):
104
+ bitrate_bps = int(format_info['bit_rate'])
105
+ else:
106
+ file_size_bytes = int(format_info.get('size', 0))
107
+ duration_seconds = float(format_info.get('duration', 0))
108
+ if file_size_bytes > 0 and duration_seconds > 0:
109
+ bitrate_bps = int((file_size_bytes * 8) / duration_seconds)
110
+
111
+ features['input_bitrate_kbps'] = bitrate_bps / 1000 if bitrate_bps > 0 else 0
112
+
113
+ features['metrics_resolution'] = f"({features['metrics_resolution_width']}, {features['metrics_resolution_height']})"
114
+
115
+ if (features['metrics_resolution_width'] > 0 and
116
+ features['metrics_resolution_height'] > 0 and
117
+ features['metrics_frame_rate'] > 0 and
118
+ features['input_bitrate_kbps'] > 0):
119
+
120
+ pixels_per_second = (features['metrics_resolution_width'] *
121
+ features['metrics_resolution_height'] *
122
+ features['metrics_frame_rate'])
123
+ features['bits_per_pixel'] = (features['input_bitrate_kbps'] * 1000) / pixels_per_second
124
+ else:
125
+ features['bits_per_pixel'] = 0
126
+
127
+ return features
128
+
129
+ except Exception as e:
130
+ if logging_enabled:
131
+ print(f"❌ Basic video property extraction failed: {e}")
132
+ return None
133
+
134
+ def extract_comprehensive_video_metrics(video_path, max_frames=150, logging_enabled=True, use_middle_section=True):
135
+ """
136
+ Extract ALL required video metrics using OpenCV analysis.
137
+
138
+ Args:
139
+ use_middle_section (bool): If True, sample from middle 60% of video
140
+ """
141
+ features = {
142
+ 'metrics_avg_motion': 0,
143
+ 'metrics_avg_edge_density': 0,
144
+ 'metrics_avg_texture': 0,
145
+ 'metrics_avg_color_complexity': 0,
146
+ 'metrics_avg_motion_variance': 0,
147
+ 'metrics_avg_grain_noise': 0
148
+ }
149
+
150
+ try:
151
+ cap = cv2.VideoCapture(video_path)
152
+ if not cap.isOpened():
153
+ if logging_enabled:
154
+ print(f"❌ Cannot open video: {video_path}")
155
+ return features
156
+
157
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
158
+ fps = cap.get(cv2.CAP_PROP_FPS)
159
+
160
+ if use_middle_section and total_frames > max_frames * 2:
161
+ start_frame = int(total_frames * 0.2) # Skip first 20%
162
+ end_frame = int(total_frames * 0.8) # Skip last 20%
163
+ effective_total = end_frame - start_frame
164
+ else:
165
+ start_frame = min(int(fps * 1.0), total_frames // 10) # Skip first second or 10%
166
+ end_frame = total_frames
167
+ effective_total = end_frame - start_frame
168
+
169
+ if effective_total > max_frames:
170
+ frame_interval = effective_total // max_frames
171
+ else:
172
+ frame_interval = 1
173
+
174
+ cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
175
+
176
+ edge_density_values = []
177
+ texture_values = []
178
+ color_complexity_values = []
179
+ spatial_info_values = []
180
+ temporal_info_values = []
181
+ grain_noise_values = []
182
+ motion_values = []
183
+
184
+ prev_frame = None
185
+ frame_count = 0
186
+ processed_frames = 0
187
+ current_frame_pos = start_frame
188
+
189
+ if logging_enabled:
190
+ print(f" 📊 Analyzing {min(max_frames, effective_total)} frames from middle section...")
191
+ print(f" 📍 Sampling range: frame {start_frame} to {end_frame} ({effective_total} frames)")
192
+
193
+ while processed_frames < max_frames:
194
+ ret, frame = cap.read()
195
+ if not ret:
196
+ break
197
+
198
+ if frame_count % frame_interval != 0:
199
+ frame_count += 1
200
+ current_frame_pos += 1
201
+ continue
202
+
203
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
204
+
205
+ edge_density = compute_edge_density(gray)
206
+ edge_density_values.append(edge_density)
207
+
208
+ texture_complexity = compute_texture_complexity(gray)
209
+ texture_values.append(texture_complexity)
210
+
211
+ color_complexity = compute_color_complexity(frame)
212
+ color_complexity_values.append(color_complexity)
213
+
214
+ spatial_info = compute_spatial_information(gray)
215
+ spatial_info_values.append(spatial_info)
216
+
217
+ grain_noise = compute_grain_noise_level(gray)
218
+ grain_noise_values.append(grain_noise)
219
+
220
+ if prev_frame is not None:
221
+ motion = compute_motion_metric(prev_frame, gray)
222
+ motion_values.append(motion)
223
+ temporal_info = compute_temporal_information(prev_frame, gray)
224
+ temporal_info_values.append(temporal_info)
225
+
226
+ prev_frame = gray.copy()
227
+ processed_frames += 1
228
+ frame_count += 1
229
+ current_frame_pos += 1
230
+
231
+ cap.release()
232
+
233
+ if edge_density_values:
234
+ features['metrics_avg_edge_density'] = np.mean(edge_density_values)
235
+
236
+ if texture_values:
237
+ features['metrics_avg_texture'] = np.mean(texture_values)
238
+
239
+ if color_complexity_values:
240
+ features['metrics_avg_color_complexity'] = np.mean(color_complexity_values)
241
+
242
+ if spatial_info_values:
243
+ features['metrics_avg_spatial_information'] = np.mean(spatial_info_values)
244
+
245
+ if grain_noise_values:
246
+ features['metrics_avg_grain_noise'] = np.mean(grain_noise_values)
247
+
248
+ if motion_values:
249
+ features['metrics_avg_motion'] = np.mean(motion_values)
250
+ features['metrics_avg_motion_variance'] = np.var(motion_values)
251
+
252
+ if temporal_info_values:
253
+ features['metrics_avg_temporal_information'] = np.mean(temporal_info_values)
254
+
255
+
256
+ if logging_enabled:
257
+ print(f" ✅ Processed {processed_frames} frames")
258
+ return features
259
+
260
+ except Exception as e:
261
+ if logging_enabled:
262
+ print(f"⚠️ Comprehensive video analysis failed: {e}")
263
+
264
+ return {
265
+ 'metrics_avg_motion': 0.1,
266
+ 'metrics_avg_edge_density': 0.05,
267
+ 'metrics_avg_texture': 4.0,
268
+ 'metrics_avg_color_complexity': 3.0,
269
+ 'metrics_avg_motion_variance': 1.0,
270
+ 'metrics_avg_grain_noise': 5.0
271
+ }
272
+
273
+ def compute_edge_density(gray_frame, threshold1=100, threshold2=200):
274
+ """Compute edge density using Canny edge detection."""
275
+ edges = cv2.Canny(gray_frame, threshold1, threshold2)
276
+ edge_pixels = np.count_nonzero(edges)
277
+ total_pixels = edges.size
278
+ return edge_pixels / total_pixels
279
+
280
+ def compute_texture_complexity(gray_frame):
281
+ """Compute texture complexity using histogram entropy."""
282
+ hist = cv2.calcHist([gray_frame], [0], None, [256], [0, 256]).ravel()
283
+ hist_norm = hist / hist.sum() # normalize histogram
284
+ entropy = -np.sum([p * np.log2(p) for p in hist_norm if p > 0])
285
+ return entropy
286
+
287
+ def compute_color_complexity(frame):
288
+ """Compute color complexity as average entropy across color channels."""
289
+ channels = cv2.split(frame)
290
+ entropies = []
291
+ for channel in channels:
292
+ hist = cv2.calcHist([channel], [0], None, [256], [0, 256]).ravel()
293
+ hist_sum = np.sum(hist)
294
+ if hist_sum > 0:
295
+ hist_norm = hist / hist_sum
296
+ entropy = -np.sum([p * np.log2(p) for p in hist_norm if p > 0])
297
+ else:
298
+ entropy = 0
299
+ entropies.append(entropy)
300
+ return np.mean(entropies)
301
+
302
+ def compute_spatial_information(gray_frame):
303
+ """Compute spatial information using Sobel gradients."""
304
+ sobelx = cv2.Sobel(gray_frame, cv2.CV_64F, 1, 0, ksize=3)
305
+ sobely = cv2.Sobel(gray_frame, cv2.CV_64F, 0, 1, ksize=3)
306
+ gradient_magnitude = np.sqrt(sobelx**2 + sobely**2)
307
+ return np.std(gradient_magnitude)
308
+
309
+ def compute_temporal_information(prev_gray, curr_gray):
310
+ """Compute temporal information as standard deviation of frame difference."""
311
+ diff = cv2.absdiff(prev_gray, curr_gray)
312
+ return np.std(diff)
313
+
314
+ def compute_motion_metric(prev_gray, curr_gray):
315
+ """Compute motion metric as normalized frame difference."""
316
+ diff = cv2.absdiff(prev_gray, curr_gray)
317
+ motion_value = np.mean(diff) / 255.0 # Normalize to 0-1
318
+ return motion_value
319
+
320
+ def compute_grain_noise_level(gray_frame, kernel_size=3):
321
+ """Compute grain/noise level using high-pass filtering."""
322
+ blurred = cv2.GaussianBlur(gray_frame, (kernel_size, kernel_size), 0)
323
+ noise = gray_frame.astype(np.float32) - blurred.astype(np.float32)
324
+ return np.std(noise)
325
+
326
+ def analyze_video_quality_metrics(video_path, num_frames=30):
327
+ """
328
+ Analyze video to determine which preprocessing filters would be beneficial.
329
+ Returns a dictionary of quality metrics and recommendations.
330
+ """
331
+ cap = cv2.VideoCapture(video_path)
332
+
333
+ if not cap.isOpened():
334
+ return None
335
+
336
+ metrics = {
337
+ 'noise_level': 0,
338
+ 'sharpness': 0,
339
+ 'contrast': 0,
340
+ 'brightness': 0,
341
+ 'color_saturation': 0,
342
+ 'motion_blur': 0,
343
+ 'compression_artifacts': 0,
344
+ 'text_content': 0,
345
+ 'edge_density': 0,
346
+ 'temporal_consistency': 0
347
+ }
348
+
349
+ frames = []
350
+ frame_count = 0
351
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
352
+ step = max(1, total_frames // num_frames)
353
+
354
+ while frame_count < num_frames:
355
+ cap.set(cv2.CAP_PROP_POS_FRAMES, frame_count * step)
356
+ ret, frame = cap.read()
357
+ if not ret:
358
+ break
359
+ frames.append(frame)
360
+ frame_count += 1
361
+
362
+ cap.release()
363
+
364
+ if not frames:
365
+ return None
366
+
367
+ # Analyze each metric
368
+ metrics.update({
369
+ 'noise_level': _analyze_noise_level(frames),
370
+ 'sharpness': _analyze_sharpness(frames),
371
+ 'contrast': _analyze_contrast(frames),
372
+ 'brightness': _analyze_brightness(frames),
373
+ 'color_saturation': _analyze_color_saturation(frames),
374
+ 'motion_blur': _analyze_motion_blur(frames),
375
+ 'compression_artifacts': _analyze_compression_artifacts(frames),
376
+ 'text_content': _analyze_text_content(frames),
377
+ 'edge_density': _analyze_edge_density(frames),
378
+ 'temporal_consistency': _analyze_temporal_consistency(frames)
379
+ })
380
+
381
+ return metrics
382
+
383
+ def _analyze_noise_level(frames):
384
+ """Estimate noise level in video frames."""
385
+ noise_scores = []
386
+
387
+ for frame in frames:
388
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
389
+
390
+ laplacian = cv2.Laplacian(gray, cv2.CV_64F)
391
+ noise_var = laplacian.var()
392
+
393
+ f_transform = np.fft.fft2(gray)
394
+ f_shift = np.fft.fftshift(f_transform)
395
+ magnitude = np.abs(f_shift)
396
+
397
+ h, w = magnitude.shape
398
+ center_h, center_w = h // 2, w // 2
399
+ high_freq_mask = np.zeros_like(magnitude)
400
+ high_freq_mask[center_h-h//8:center_h+h//8, center_w-w//8:center_w+w//8] = 1
401
+ high_freq_energy = np.sum(magnitude * (1 - high_freq_mask)) / np.sum(magnitude)
402
+
403
+ noise_scores.append(min(noise_var / 1000, high_freq_energy * 2))
404
+
405
+ return np.mean(noise_scores)
406
+
407
+ def _analyze_sharpness(frames):
408
+ """Analyze image sharpness using multiple methods."""
409
+ sharpness_scores = []
410
+
411
+ for frame in frames:
412
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
413
+
414
+ laplacian_var = cv2.Laplacian(gray, cv2.CV_64F).var()
415
+
416
+ sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
417
+ sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
418
+ tenengrad = np.mean(sobelx**2 + sobely**2)
419
+
420
+ combined_sharpness = (laplacian_var / 2000 + tenengrad / 50000) / 2
421
+ sharpness_scores.append(min(combined_sharpness, 1.0))
422
+
423
+ return np.mean(sharpness_scores)
424
+
425
+ def _analyze_contrast(frames):
426
+ """Analyze contrast using RMS contrast and histogram analysis."""
427
+ contrast_scores = []
428
+
429
+ for frame in frames:
430
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
431
+
432
+ rms_contrast = np.std(gray) / np.mean(gray) if np.mean(gray) > 0 else 0
433
+
434
+ hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
435
+ hist_norm = hist / hist.sum()
436
+
437
+ pixels_in_range = []
438
+ for i in range(0, 256, 32):
439
+ pixels_in_range.append(np.sum(hist_norm[i:i+32]))
440
+
441
+ hist_spread = np.std(pixels_in_range)
442
+
443
+ combined_contrast = (rms_contrast / 100 + hist_spread * 3) / 2
444
+ contrast_scores.append(min(combined_contrast, 1.0))
445
+
446
+ return np.mean(contrast_scores)
447
+
448
+ def _analyze_brightness(frames):
449
+ """Analyze brightness distribution."""
450
+ brightness_scores = []
451
+
452
+ for frame in frames:
453
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
454
+ mean_brightness = np.mean(gray) / 255.0
455
+ brightness_scores.append(mean_brightness)
456
+
457
+ return np.mean(brightness_scores)
458
+
459
+ def _analyze_color_saturation(frames):
460
+ """Analyze color saturation levels."""
461
+ saturation_scores = []
462
+
463
+ for frame in frames:
464
+ hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
465
+ saturation = hsv[:, :, 1]
466
+ mean_saturation = np.mean(saturation) / 255.0
467
+ saturation_scores.append(mean_saturation)
468
+
469
+ return np.mean(saturation_scores)
470
+
471
+ def _analyze_motion_blur(frames):
472
+ """Detect motion blur using edge analysis."""
473
+ if len(frames) < 2:
474
+ return 0
475
+
476
+ blur_scores = []
477
+
478
+ for i in range(len(frames) - 1):
479
+ gray1 = cv2.cvtColor(frames[i], cv2.COLOR_BGR2GRAY)
480
+ gray2 = cv2.cvtColor(frames[i + 1], cv2.COLOR_BGR2GRAY)
481
+
482
+ edges1 = cv2.Canny(gray1, 50, 150)
483
+ edges2 = cv2.Canny(gray2, 50, 150)
484
+
485
+
486
+ edge_diff = np.sum(np.abs(edges1.astype(float) - edges2.astype(float)))
487
+ frame_size = gray1.shape[0] * gray1.shape[1]
488
+ blur_score = edge_diff / frame_size / 255.0
489
+
490
+ blur_scores.append(blur_score)
491
+
492
+ return np.mean(blur_scores)
493
+
494
+ def _analyze_compression_artifacts(frames):
495
+ """Detect compression artifacts using blockiness detection."""
496
+ artifact_scores = []
497
+
498
+ for frame in frames:
499
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
500
+
501
+ block_diffs = []
502
+ h, w = gray.shape
503
+
504
+ for y in range(0, h - 8, 8):
505
+ for x in range(0, w - 8, 8):
506
+ block = gray[y:y+8, x:x+8]
507
+
508
+ if x + 8 < w:
509
+ right_block = gray[y:y+8, x+8:x+16] if x+16 <= w else None
510
+ if right_block is not None:
511
+ edge_diff = np.mean(np.abs(block[:, -1].astype(float) - right_block[:, 0].astype(float)))
512
+ block_diffs.append(edge_diff)
513
+
514
+ if block_diffs:
515
+ artifact_score = np.mean(block_diffs) / 255.0
516
+ artifact_scores.append(artifact_score)
517
+
518
+ return np.mean(artifact_scores) if artifact_scores else 0
519
+
520
+ def _analyze_text_content(frames):
521
+ """Detect text/high-contrast content."""
522
+ text_scores = []
523
+
524
+ for frame in frames:
525
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
526
+
527
+ edges = cv2.Canny(gray, 100, 200)
528
+ edge_density = np.sum(edges > 0) / (edges.shape[0] * edges.shape[1])
529
+
530
+ _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
531
+ contrast_regions = np.sum(binary > 0) / (binary.shape[0] * binary.shape[1])
532
+
533
+ text_score = (edge_density * 2 + abs(contrast_regions - 0.5) * 2)
534
+ text_scores.append(min(text_score, 1.0))
535
+
536
+ return np.mean(text_scores)
537
+
538
+ def _analyze_edge_density(frames):
539
+ """Analyze edge density for detail preservation."""
540
+ edge_scores = []
541
+
542
+ for frame in frames:
543
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
544
+ edges = cv2.Canny(gray, 50, 150)
545
+ edge_density = np.sum(edges > 0) / (edges.shape[0] * edges.shape[1])
546
+ edge_scores.append(edge_density)
547
+
548
+ return np.mean(edge_scores)
549
+
550
+ def _analyze_temporal_consistency(frames):
551
+ """Analyze temporal consistency between frames."""
552
+ if len(frames) < 3:
553
+ return 1.0
554
+
555
+ consistency_scores = []
556
+
557
+ for i in range(1, len(frames) - 1):
558
+ prev_gray = cv2.cvtColor(frames[i-1], cv2.COLOR_BGR2GRAY)
559
+ curr_gray = cv2.cvtColor(frames[i], cv2.COLOR_BGR2GRAY)
560
+ next_gray = cv2.cvtColor(frames[i+1], cv2.COLOR_BGR2GRAY)
561
+
562
+ diff1 = np.mean(np.abs(curr_gray.astype(float) - prev_gray.astype(float)))
563
+ diff2 = np.mean(np.abs(next_gray.astype(float) - curr_gray.astype(float)))
564
+
565
+ consistency = 1.0 - min(abs(diff1 - diff2) / 255.0, 1.0)
566
+ consistency_scores.append(consistency)
567
+
568
+ return np.mean(consistency_scores)
services/compress/utils/calculate_vmaf_adv.py ADDED
@@ -0,0 +1,734 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import json
4
+ import tempfile
5
+ import subprocess
6
+ import concurrent.futures
7
+ from ffmpeg_quality_metrics import FfmpegQualityMetrics
8
+
9
+ def _extract_clip_optimized(input_file, output_file, start_time, duration, logging_enabled=False):
10
+ """Extract clip with keyframe-aware seeking for better quality."""
11
+ try:
12
+ input_ext = os.path.splitext(input_file)[1].lower()
13
+ raw_formats = ['.y4m', '.yuv', '.raw', '.rgb']
14
+ is_raw_format = input_ext in raw_formats
15
+
16
+ if is_raw_format:
17
+ cmd = [
18
+ 'ffmpeg', '-y', '-ss', str(start_time), '-i', input_file,
19
+ '-t', str(duration),
20
+ '-c:v', 'libx264', '-preset', 'ultrafast', '-qp', '0',
21
+ '-pix_fmt', 'yuv420p', '-an', output_file
22
+ ]
23
+ else:
24
+ cmd = [
25
+ 'ffmpeg', '-y',
26
+ '-ss', str(max(0, start_time - 1)),
27
+ '-i', input_file,
28
+ '-ss', '1',
29
+ '-t', str(duration),
30
+ '-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '10',
31
+ '-pix_fmt', 'yuv420p', '-an', output_file
32
+ ]
33
+
34
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
35
+ success = result.returncode == 0 and os.path.exists(output_file) and os.path.getsize(output_file) > 0
36
+
37
+ if logging_enabled:
38
+ method = "lossless encoding" if is_raw_format else "keyframe-aligned encoding"
39
+ status = "✅" if success else "❌"
40
+ print(f" {status} Clip extraction ({method}): {start_time}s")
41
+
42
+ return success
43
+
44
+ except Exception as e:
45
+ if logging_enabled:
46
+ print(f" ❌ Clip extraction error: {e}")
47
+ return False
48
+
49
+ def extract_vmaf_clips_with_keyframe_detection(input_file, encoded_file, num_clips=5, clip_duration=3, logging_enabled=True):
50
+ """Enhanced clip extraction with proper keyframe alignment."""
51
+
52
+ try:
53
+ cmd = [
54
+ 'ffprobe', '-v', 'quiet', '-show_entries', 'format=duration',
55
+ '-of', 'json', input_file
56
+ ]
57
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
58
+ data = json.loads(result.stdout)
59
+ duration = float(data['format']['duration'])
60
+
61
+ if duration < clip_duration * num_clips:
62
+ if logging_enabled:
63
+ print(f" ⚠️ Video too short ({duration:.1f}s) for {num_clips} clips")
64
+ return None
65
+
66
+ except Exception as e:
67
+ if logging_enabled:
68
+ print(f" ❌ Could not determine video duration: {e}")
69
+ return None
70
+
71
+ try:
72
+ cmd = [
73
+ 'ffprobe', '-v', 'quiet', '-select_streams', 'v:0',
74
+ '-show_frames', '-show_entries', 'frame=pts_time,key_frame',
75
+ '-of', 'csv=p=0', input_file
76
+ ]
77
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
78
+
79
+ keyframes = []
80
+ for line in result.stdout.strip().split('\n'):
81
+ if line:
82
+ parts = line.split(',')
83
+ if len(parts) >= 2 and parts[1] == '1': # key_frame=1
84
+ keyframes.append(float(parts[0]))
85
+
86
+ if logging_enabled:
87
+ print(f" 🔑 Found {len(keyframes)} keyframes")
88
+
89
+ except Exception as e:
90
+ if logging_enabled:
91
+ print(f" ⚠️ Keyframe detection failed: {e}, using standard timing")
92
+ keyframes = []
93
+
94
+ clip_positions = []
95
+
96
+ target_positions = []
97
+ for i in range(num_clips):
98
+ position = 0.15 + (i * (0.85 - 0.15) / (num_clips - 1)) if num_clips > 1 else 0.5
99
+ target_positions.append(duration * position)
100
+
101
+ for target_pos in target_positions:
102
+ if keyframes:
103
+ nearest_keyframe = min(keyframes, key=lambda x: abs(x - target_pos))
104
+ if nearest_keyframe + clip_duration <= duration:
105
+ clip_positions.append(nearest_keyframe)
106
+ else:
107
+ valid_keyframes = [kf for kf in keyframes if kf + clip_duration <= duration]
108
+ if valid_keyframes:
109
+ clip_positions.append(max(valid_keyframes))
110
+ else:
111
+ clip_positions.append(max(0, duration - clip_duration))
112
+ else:
113
+ clip_positions.append(max(0, min(target_pos, duration - clip_duration)))
114
+
115
+ if logging_enabled:
116
+ print(f" 📍 Clip positions: {[f'{pos:.2f}s' for pos in clip_positions]}")
117
+
118
+ vmaf_scores = []
119
+
120
+ for i, start_time in enumerate(clip_positions):
121
+ if logging_enabled:
122
+ print(f" 🎬 Extracting clip {i+1} at {start_time:.2f}s...")
123
+
124
+ temp_dir = tempfile.mkdtemp()
125
+ ref_clip = os.path.join(temp_dir, f"ref_clip_{i+1}.mp4")
126
+ enc_clip = os.path.join(temp_dir, f"enc_clip_{i+1}.mp4")
127
+
128
+ try:
129
+ ref_cmd = [
130
+ 'ffmpeg', '-y', '-ss', str(start_time), '-i', input_file,
131
+ '-t', str(clip_duration), '-c:v', 'libx264', '-preset', 'ultrafast',
132
+ '-crf', '10', '-force_key_frames', 'expr:gte(t,0)',
133
+ '-pix_fmt', 'yuv420p', '-avoid_negative_ts', 'make_zero', ref_clip
134
+ ]
135
+
136
+ enc_cmd = [
137
+ 'ffmpeg', '-y', '-ss', str(start_time), '-i', encoded_file,
138
+ '-t', str(clip_duration), '-c:v', 'libx264', '-preset', 'ultrafast',
139
+ '-crf', '15', '-pix_fmt', 'yuv420p', '-avoid_negative_ts', 'make_zero', enc_clip
140
+ ]
141
+
142
+ ref_result = subprocess.run(ref_cmd, capture_output=True, text=True, timeout=60)
143
+ enc_result = subprocess.run(enc_cmd, capture_output=True, text=True, timeout=60)
144
+
145
+ if ref_result.returncode == 0 and enc_result.returncode == 0:
146
+ from ffmpeg_quality_metrics import FfmpegQualityMetrics
147
+
148
+ ffqm = FfmpegQualityMetrics(ref_clip, enc_clip)
149
+ metrics = ffqm.calculate(["vmaf"])
150
+
151
+ if 'vmaf' in metrics and metrics['vmaf']:
152
+ clip_vmaf = sum([frame["vmaf"] for frame in metrics["vmaf"]]) / len(metrics["vmaf"])
153
+ vmaf_scores.append(round(clip_vmaf, 2))
154
+
155
+ if logging_enabled:
156
+ print(f" ✅ Clip {i+1} VMAF: {clip_vmaf:.2f}")
157
+ else:
158
+ if logging_enabled:
159
+ print(f" ❌ Clip {i+1} VMAF calculation failed")
160
+ else:
161
+ if logging_enabled:
162
+ print(f" ❌ Clip {i+1} extraction failed")
163
+
164
+ except Exception as e:
165
+ if logging_enabled:
166
+ print(f" ❌ Clip {i+1} processing failed: {e}")
167
+ finally:
168
+ # Cleanup
169
+ for temp_file in [ref_clip, enc_clip]:
170
+ if os.path.exists(temp_file):
171
+ try:
172
+ os.remove(temp_file)
173
+ except:
174
+ pass
175
+ try:
176
+ os.rmdir(temp_dir)
177
+ except:
178
+ pass
179
+
180
+ if vmaf_scores:
181
+ if len(vmaf_scores) >= 3:
182
+ sorted_scores = sorted(vmaf_scores)
183
+ median_score = sorted_scores[len(sorted_scores)//2]
184
+ min_score = sorted_scores[0]
185
+ max_score = sorted_scores[-1]
186
+
187
+ outliers = [s for s in vmaf_scores if abs(s - median_score) > 15]
188
+
189
+ if outliers and logging_enabled:
190
+ print(f" ⚠️ Detected outlier scores: {outliers}")
191
+ print(f" 📊 Score distribution: min={min_score:.1f}, median={median_score:.1f}, max={max_score:.1f}")
192
+
193
+ filtered_scores = [s for s in vmaf_scores if abs(s - median_score) <= 15]
194
+ if len(filtered_scores) >= 2:
195
+ final_vmaf = sum(filtered_scores) / len(filtered_scores)
196
+ print(f" 🔧 Using filtered scores (removed {len(outliers)} outliers): {final_vmaf:.2f}")
197
+ return final_vmaf
198
+
199
+ final_vmaf = sum(vmaf_scores) / len(vmaf_scores)
200
+ if logging_enabled:
201
+ print(f" 📊 Individual scores: {[f'{s:.2f}' for s in vmaf_scores]}")
202
+ print(f" 📊 Average VMAF: {final_vmaf:.2f}")
203
+ return final_vmaf
204
+ else:
205
+ return None
206
+
207
+ def calculate_vmaf_advanced(input_file, encoded_file,
208
+ use_sampling=True, num_clips=3, clip_duration=2,
209
+ use_downscaling=False, scale_factor=0.5,
210
+ use_parallel=False, # This parameter is for the caller of parallel, not used inside this single func
211
+ use_vmafneg=False,
212
+ default_vmaf_model_path_config: str = None,
213
+ vmafneg_model_path_config: str = None,
214
+ use_frame_rate_scaling=False,
215
+ target_fps=15.0,
216
+ frame_rate_scaling_method='uniform',
217
+ logger=None,logging_enabled=True):
218
+ """
219
+ Calculate VMAF with multiple sampling methods
220
+
221
+ Args:
222
+ input_file: Path to the reference video file
223
+ encoded_file: Path to the encoded video file
224
+ use_sampling: Whether to use sampling for VMAF calculation
225
+ num_clips: Number of clips to sample for VMAF calculation
226
+ clip_duration: Duration of each clip in seconds
227
+ use_downscaling: Whether to downscale videos for faster VMAF calculation
228
+ scale_factor: Scale factor for downscaling (0.5 = 50% resolution)
229
+ use_vmafneg: Whether to use VMAFNEG model
230
+ default_vmaf_model_path_config: Full path to the default VMAF model.
231
+ vmafneg_model_path_config: Full path to the VMAFNEG model.
232
+ logger: Optional logger instance for logging messages
233
+ Returns:
234
+ VMAF score as a float, or None if calculation fails
235
+
236
+ """
237
+ def log_message(message, level="info"):
238
+ if logger:
239
+ if level == "error": logger.error(message)
240
+ elif level == "warning": logger.warning(message)
241
+ elif level == "debug" and hasattr(logger, 'debug'): logger.debug(message)
242
+ else: logger.info(message)
243
+ else:
244
+ print(message) # Fallback if no logger
245
+
246
+ if not os.path.exists(input_file):
247
+ log_message(f"Error: Input file '{input_file}' does not exist", "error")
248
+ return None
249
+
250
+ if not os.path.exists(encoded_file):
251
+ log_message(f"Error: Encoded file '{encoded_file}' does not exist", "error")
252
+ return None
253
+
254
+ if use_frame_rate_scaling:
255
+ log_message(f"Using frame rate scaling: {target_fps} FPS with {frame_rate_scaling_method} method")
256
+
257
+ # Determine which VMAF model path to use directly from parameters
258
+ target_model_path = None
259
+ model_type_for_log = "libvmaf_default" # Default assumption
260
+
261
+ if use_vmafneg:
262
+ log_message(f"VMAFNEG flag is True. Using VMAFNEG model path: '{vmafneg_model_path_config}'", "debug")
263
+ target_model_path = vmafneg_model_path_config
264
+ model_type_for_log = "VMAFNEG"
265
+ else:
266
+ log_message(f"VMAFNEG flag is False. Using Default VMAF model path: '{default_vmaf_model_path_config}'", "debug")
267
+ target_model_path = default_vmaf_model_path_config
268
+ model_type_for_log = "Default VMAF"
269
+
270
+ vmaf_options_dict = None
271
+
272
+ if target_model_path and os.path.exists(target_model_path):
273
+ log_message(f"Using VMAF model ({model_type_for_log}): {target_model_path}")
274
+ vmaf_options_dict = {"model_path": target_model_path}
275
+ elif target_model_path: # Path provided but not found
276
+ log_message(f"Warning: Specified {model_type_for_log} model file not found at '{target_model_path}'. "
277
+ "ffmpeg-quality-metrics will use libvmaf's default model.", "warning")
278
+ model_type_for_log = "libvmaf_default (model file not found)" # Update log type
279
+ else: # No path provided for the selected option
280
+ log_message(f"Warning: No model path provided for {model_type_for_log} configuration. "
281
+ "ffmpeg-quality-metrics will use libvmaf's default model.", "warning")
282
+ model_type_for_log = "libvmaf_default (no model path provided)" # Update log type
283
+
284
+ log_message(f"Effective VMAF model for this calculation: {model_type_for_log}", "debug")
285
+
286
+ temp_dir = tempfile.mkdtemp()
287
+ temp_files = []
288
+
289
+ def extract_vmaf_score(metrics):
290
+ """Extract VMAF score from metrics dictionary"""
291
+ try:
292
+ if 'vmaf' in metrics and metrics['vmaf']:
293
+ # Calculate average of frame VMAF scores
294
+ vmaf_score = sum([frame["vmaf"] for frame in metrics["vmaf"]]) / len(metrics["vmaf"])
295
+ return round(vmaf_score, 2)
296
+ else:
297
+ print("No VMAF data found in metrics")
298
+ return None
299
+ except Exception as e:
300
+ print(f"Error extracting VMAF score: {e}")
301
+ return None
302
+
303
+ try:
304
+ # ---------- Handle downscaling if enabled ----------
305
+ if use_downscaling:
306
+ if logging_enabled:
307
+ print(f"Downscaling videos to {int(scale_factor * 100)}% for faster VMAF calculation")
308
+
309
+ # ✅ Check if input is raw/uncompressed format
310
+ input_ext = os.path.splitext(input_file)[1].lower()
311
+ encoded_ext = os.path.splitext(encoded_file)[1].lower()
312
+
313
+ # Raw formats that cannot use stream copy
314
+ raw_formats = ['.y4m', '.yuv', '.raw', '.rgb']
315
+
316
+ is_input_raw = input_ext in raw_formats
317
+ is_encoded_raw = encoded_ext in raw_formats
318
+
319
+ scaled_ref = os.path.join(temp_dir, "ref_scaled.mp4")
320
+ scaled_enc = os.path.join(temp_dir, "enc_scaled.mp4")
321
+ temp_files.extend([scaled_ref, scaled_enc])
322
+
323
+ # Get video dimensions
324
+ cmd = [
325
+ 'ffprobe', '-v', 'error', '-select_streams', 'v:0',
326
+ '-show_entries', 'stream=width,height', '-of', 'json', input_file
327
+ ]
328
+ result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
329
+ data = json.loads(result.stdout)
330
+
331
+ try:
332
+ width = int(int(data['streams'][0]['width']) * scale_factor)
333
+ height = int(int(data['streams'][0]['height']) * scale_factor)
334
+
335
+ # Must be even numbers for YUV formats
336
+ width = width - (width % 2)
337
+ height = height - (height % 2)
338
+
339
+ # ✅ Smart codec selection for reference video
340
+ if is_input_raw:
341
+ # Raw format - must encode (use lossless to preserve quality)
342
+ if logging_enabled:
343
+ print(f" 📹 Raw format detected ({input_ext}) - using lossless encoding")
344
+ ref_cmd = [
345
+ 'ffmpeg', '-y', '-i', input_file,
346
+ '-vf', f'scale={width}:{height}',
347
+ '-c:v', 'libx264', '-preset', 'ultrafast', '-qp', '0', # Lossless
348
+ '-pix_fmt', 'yuv420p', scaled_ref
349
+ ]
350
+ else:
351
+ # Already encoded - try to use stream copy with scaling
352
+ if logging_enabled:
353
+ print(f" 📼 Encoded format detected ({input_ext}) - trying stream copy with scaling")
354
+ ref_cmd = [
355
+ 'ffmpeg', '-y', '-i', input_file,
356
+ '-vf', f'scale={width}:{height}',
357
+ '-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '10', # Very high quality fallback
358
+ '-pix_fmt', 'yuv420p', scaled_ref
359
+ ]
360
+
361
+ if is_encoded_raw:
362
+ # Raw format - must encode
363
+ enc_cmd = [
364
+ 'ffmpeg', '-y', '-i', encoded_file,
365
+ '-vf', f'scale={width}:{height}',
366
+ '-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '15', # High quality
367
+ '-pix_fmt', 'yuv420p', scaled_enc
368
+ ]
369
+ else:
370
+ # Already encoded - use moderate quality
371
+ enc_cmd = [
372
+ 'ffmpeg', '-y', '-i', encoded_file,
373
+ '-vf', f'scale={width}:{height}',
374
+ '-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '18', # Standard quality
375
+ '-pix_fmt', 'yuv420p', scaled_enc
376
+ ]
377
+
378
+ # Execute commands
379
+ ref_result = subprocess.run(ref_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
380
+ enc_result = subprocess.run(enc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
381
+
382
+ if ref_result.returncode == 0 and enc_result.returncode == 0:
383
+ # Replace original paths with scaled versions
384
+ input_file = scaled_ref
385
+ encoded_file = scaled_enc
386
+ if logging_enabled:
387
+ quality_note = "lossless reference" if is_input_raw else "high quality reference"
388
+ print(f" ✅ Videos downscaled to {width}x{height} ({quality_note})")
389
+ else:
390
+ if logging_enabled:
391
+ print(f" ❌ Error downscaling videos, using original resolution")
392
+ if ref_result.returncode != 0:
393
+ print(f" Reference error: {ref_result.stderr.decode()}")
394
+ if enc_result.returncode != 0:
395
+ print(f" Encoded error: {enc_result.stderr.decode()}")
396
+
397
+ except (KeyError, ValueError, subprocess.SubprocessError) as e:
398
+ if logging_enabled:
399
+ print(f" ❌ Error downscaling videos, using original resolution: {e}")
400
+
401
+
402
+ if use_frame_rate_scaling:
403
+ if logging_enabled:
404
+ print(f"Scaling frame rate to {target_fps} FPS for faster VMAF calculation")
405
+
406
+ # Apply frame rate scaling to both input and encoded videos
407
+ fps_scaled_ref, fps_scaled_enc = apply_frame_rate_scaling(
408
+ input_file, encoded_file, target_fps, frame_rate_scaling_method, temp_dir, logging_enabled
409
+ )
410
+
411
+ if fps_scaled_ref and fps_scaled_enc:
412
+ temp_files.extend([fps_scaled_ref, fps_scaled_enc])
413
+ # Update file paths to use frame rate scaled versions
414
+ input_file = fps_scaled_ref
415
+ encoded_file = fps_scaled_enc
416
+ if logging_enabled:
417
+ print(f"Videos scaled to {target_fps} FPS for VMAF calculation")
418
+ else:
419
+ log_message(f"Frame rate scaling failed, using original frame rate", "warning")
420
+
421
+ # ---------- Handle clip sampling if enabled ----------
422
+ if use_sampling:
423
+ vmaf_score = extract_vmaf_clips_with_keyframe_detection(
424
+ input_file=input_file,
425
+ encoded_file=encoded_file,
426
+ num_clips=num_clips,
427
+ clip_duration=clip_duration,
428
+ logging_enabled=logging_enabled
429
+ )
430
+
431
+ if vmaf_score is not None:
432
+ return vmaf_score
433
+ else:
434
+ if logging_enabled:
435
+ print("Enhanced sampling failed, falling back to original method...")
436
+
437
+ # Get video duration
438
+ cmd = [
439
+ 'ffprobe', '-v', 'error', '-show_entries', 'format=duration',
440
+ '-of', 'json', encoded_file
441
+ ]
442
+ result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
443
+ data = json.loads(result.stdout)
444
+
445
+ try:
446
+ duration = float(data['format']['duration'])
447
+ print(f"Video duration: {duration}s")
448
+ except (KeyError, ValueError) as e:
449
+ print(f"Could not determine video duration: {e}, falling back to full calculation")
450
+ # Calculate VMAF on full videos
451
+ ffqm = FfmpegQualityMetrics(input_file, encoded_file)
452
+ metrics = ffqm.calculate(["vmaf"], vmaf_options=vmaf_options_dict) # Pass model options
453
+ return extract_vmaf_score(metrics)
454
+
455
+ # Skip sampling for short videos
456
+ if duration <= num_clips * clip_duration * 2:
457
+ log_message(f"Video too short ({duration}s), calculating full VMAF")
458
+ ffqm = FfmpegQualityMetrics(input_file, encoded_file)
459
+ metrics = ffqm.calculate(["vmaf"], vmaf_options=vmaf_options_dict) # Pass model options
460
+ return extract_vmaf_score(metrics)
461
+
462
+ # Calculate strategic sample points (beginning, middle, end)
463
+ sample_points = []
464
+
465
+ # Beginning segment (first 30%)
466
+ if num_clips >= 1:
467
+ begin_point = duration * 0.1 # 10% into the video
468
+ sample_points.append(begin_point)
469
+
470
+ # Middle segment (middle 40%)
471
+ if num_clips >= 2:
472
+ middle_point = duration * 0.5 # 50% into the video
473
+ sample_points.append(middle_point)
474
+
475
+ # End segment (last 30%)
476
+ if num_clips >= 3:
477
+ end_point = duration * 0.9 # 90% into the video
478
+ sample_points.append(end_point)
479
+
480
+ # Add more evenly distributed points if requested
481
+ if num_clips > 3:
482
+ for i in range(4, num_clips + 1):
483
+ pos = duration * (i - 0.5) / (num_clips + 1)
484
+ sample_points.append(pos)
485
+
486
+ print(f"Using {len(sample_points)} clips for VMAF calculation")
487
+
488
+ # ---------- Define clip processing function for parallel/sequential use ----------
489
+ def process_clip(start_time):
490
+ """Process a single clip for VMAF calculation"""
491
+ # Adjust start time to ensure we don't go beyond video duration
492
+ start_time = max(0, min(start_time, duration - clip_duration))
493
+
494
+ # Create temp files for this clip
495
+ ref_clip = os.path.join(temp_dir, f"ref_clip_{start_time:.2f}.mp4")
496
+ enc_clip = os.path.join(temp_dir, f"enc_clip_{start_time:.2f}.mp4")
497
+
498
+ # ✅ Use optimized extraction
499
+ ref_success = _extract_clip_optimized(input_file, ref_clip, start_time, clip_duration, logging_enabled)
500
+ if not ref_success:
501
+ print(f"Failed to extract reference clip at {start_time}s")
502
+ return None
503
+
504
+ enc_success = _extract_clip_optimized(encoded_file, enc_clip, start_time, clip_duration, logging_enabled)
505
+ if not enc_success:
506
+ print(f"Failed to extract encoded clip at {start_time}s")
507
+ return None
508
+
509
+ temp_files.extend([ref_clip, enc_clip])
510
+
511
+ # Calculate VMAF for this clip
512
+ try:
513
+ ffqm = FfmpegQualityMetrics(ref_clip, enc_clip)
514
+ metrics = ffqm.calculate(["vmaf"], vmaf_options=vmaf_options_dict) # Pass model options
515
+
516
+ # Extract VMAF score using our helper function
517
+ clip_vmaf = extract_vmaf_score(metrics)
518
+ if clip_vmaf is not None:
519
+ print(f"Clip at {start_time:.2f}s VMAF: {clip_vmaf}")
520
+ return clip_vmaf
521
+ except Exception as e:
522
+ print(f"Error calculating VMAF for clip at {start_time:.2f}s: {e}")
523
+ return None
524
+
525
+ # ---------- Process clips in parallel or sequentially ----------
526
+ vmaf_scores = []
527
+
528
+ if use_parallel:
529
+ # Process clips in parallel
530
+ with concurrent.futures.ThreadPoolExecutor(max_workers=min(num_clips, os.cpu_count())) as executor:
531
+ futures = [executor.submit(process_clip, start_time) for start_time in sample_points]
532
+
533
+ for future in concurrent.futures.as_completed(futures):
534
+ try:
535
+ result = future.result()
536
+ if result is not None:
537
+ vmaf_scores.append(result)
538
+ except Exception as e:
539
+ print(f"Error processing clip: {e}")
540
+ else:
541
+ # Process clips sequentially
542
+ for start_time in sample_points:
543
+ result = process_clip(start_time)
544
+ if result is not None:
545
+ vmaf_scores.append(result)
546
+
547
+ # Average the scores
548
+ if vmaf_scores:
549
+ avg_vmaf = sum(vmaf_scores) / len(vmaf_scores)
550
+ return round(avg_vmaf, 2)
551
+ else:
552
+ print("No valid VMAF scores calculated from clips, trying direct calculation")
553
+ # Fall back to original calculation
554
+ try:
555
+ ffqm = FfmpegQualityMetrics(input_file, encoded_file)
556
+ metrics = ffqm.calculate(["vmaf"], vmaf_options=vmaf_options_dict) # Pass model options
557
+ return extract_vmaf_score(metrics)
558
+ except Exception as e:
559
+ print(f"Direct VMAF calculation failed: {e}")
560
+ return None
561
+ else:
562
+ # No sampling, calculate VMAF on entire video
563
+ try:
564
+ print("Calculating full VMAF (no sampling)...")
565
+ ffqm = FfmpegQualityMetrics(input_file, encoded_file)
566
+ metrics = ffqm.calculate(["vmaf"], vmaf_options=vmaf_options_dict) # Pass model options
567
+ return extract_vmaf_score(metrics)
568
+ except Exception as e:
569
+ print(f"Full VMAF calculation failed: {e}")
570
+ return None
571
+
572
+ except Exception as e:
573
+ log_message(f"Error in VMAF calculation: {e}", "error")
574
+ # Fall back to direct FFmpeg VMAF calculation
575
+ try:
576
+ log_message("Attempting direct FFmpeg VMAF calculation...")
577
+ libvmaf_options_list = ["log_fmt=json"]
578
+ if target_model_path and os.path.exists(target_model_path): # Check target_model_path directly
579
+ model_path_for_cli = target_model_path.replace("\\", "/")
580
+ libvmaf_options_list.append(f"model_path='{model_path_for_cli}'")
581
+ libvmaf_filter_options = ":".join(libvmaf_options_list)
582
+ cmd = [
583
+ 'ffmpeg', '-i', input_file, '-i', encoded_file,
584
+ '-filter_complex', f'[0:v]setpts=PTS-STARTPTS[reference];[1:v]setpts=PTS-STARTPTS[distorted];[reference][distorted]libvmaf={libvmaf_filter_options}',
585
+ '-f', 'null', '-'
586
+ ]
587
+
588
+ result = subprocess.run(cmd, capture_output=True, text=True)
589
+
590
+ # Extract VMAF score from output
591
+ for line in result.stderr.splitlines():
592
+ if "VMAF score:" in line:
593
+ score = float(line.split("VMAF score:")[1].strip())
594
+ print(f"Direct FFmpeg VMAF score: {score}")
595
+ return round(score, 2)
596
+
597
+ print("Could not extract VMAF score from FFmpeg output")
598
+ return None
599
+ except Exception as e2:
600
+ print(f"Fatal error calculating VMAF: {e2}")
601
+ return None
602
+
603
+ finally:
604
+ # Clean up temp files and directory
605
+ for file in temp_files:
606
+ try:
607
+ if os.path.exists(file):
608
+ os.unlink(file)
609
+ except Exception as e:
610
+ print(f"Error cleaning up temp file {file}: {e}")
611
+
612
+ try:
613
+ os.rmdir(temp_dir)
614
+ except Exception as e:
615
+ print(f"Error removing temp directory: {e}")
616
+
617
+ def apply_frame_rate_scaling(input_file, encoded_file, target_fps, scaling_method, temp_dir, logging_enabled=True):
618
+ """Apply frame rate scaling while preserving reference quality."""
619
+ try:
620
+ import subprocess
621
+ import json
622
+
623
+ # Get original video properties
624
+ def get_video_info(video_path):
625
+ cmd = [
626
+ 'ffprobe', '-v', 'quiet', '-print_format', 'json',
627
+ '-show_streams', '-select_streams', 'v:0', video_path
628
+ ]
629
+ result = subprocess.run(cmd, capture_output=True, text=True)
630
+ if result.returncode == 0:
631
+ data = json.loads(result.stdout)
632
+ stream = data['streams'][0]
633
+ fps_str = stream.get('r_frame_rate', '30/1')
634
+ if '/' in fps_str:
635
+ num, den = fps_str.split('/')
636
+ fps = float(num) / float(den) if float(den) != 0 else 30
637
+ else:
638
+ fps = float(fps_str)
639
+ return fps
640
+ return None
641
+
642
+ # Get original frame rates
643
+ ref_fps = get_video_info(input_file)
644
+ enc_fps = get_video_info(encoded_file)
645
+
646
+ if not ref_fps or not enc_fps:
647
+ if logging_enabled:
648
+ print("Could not determine original frame rates")
649
+ return None, None
650
+
651
+ # Check if scaling is beneficial
652
+ min_original_fps = min(ref_fps, enc_fps)
653
+ if target_fps >= min_original_fps:
654
+ if logging_enabled:
655
+ print(f"Target FPS ({target_fps}) >= original FPS ({min_original_fps}), skipping frame rate scaling")
656
+ return input_file, encoded_file # Return original files
657
+
658
+ # Create output paths
659
+ scaled_ref = os.path.join(temp_dir, "ref_fps_scaled.mp4")
660
+ scaled_enc = os.path.join(temp_dir, "enc_fps_scaled.mp4")
661
+
662
+ # Build FFmpeg filter based on scaling method
663
+ if scaling_method == 'uniform':
664
+ filter_complex = f'fps={target_fps}'
665
+ elif scaling_method == 'smart':
666
+ interval = max(1, int(min_original_fps / target_fps))
667
+ filter_complex = f'select=not(mod(n\\,{interval})),setpts=N/FRAME_RATE/TB'
668
+ else:
669
+ filter_complex = f'fps={target_fps}'
670
+
671
+ # ✅ Smart codec selection based on input format
672
+ input_ext = os.path.splitext(input_file)[1].lower()
673
+ encoded_ext = os.path.splitext(encoded_file)[1].lower()
674
+
675
+ raw_formats = ['.y4m', '.yuv', '.raw', '.rgb']
676
+ is_input_raw = input_ext in raw_formats
677
+ is_encoded_raw = encoded_ext in raw_formats
678
+
679
+ # Scale reference video with appropriate quality
680
+ if is_input_raw:
681
+ # Raw format - use lossless encoding
682
+ ref_cmd = [
683
+ 'ffmpeg', '-y', '-i', input_file,
684
+ '-vf', filter_complex,
685
+ '-c:v', 'libx264', '-preset', 'ultrafast', '-qp', '0', # Lossless
686
+ '-pix_fmt', 'yuv420p', scaled_ref
687
+ ]
688
+ else:
689
+ # Already encoded - use very high quality
690
+ ref_cmd = [
691
+ 'ffmpeg', '-y', '-i', input_file,
692
+ '-vf', filter_complex,
693
+ '-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '10', # Very high quality
694
+ '-pix_fmt', 'yuv420p', scaled_ref
695
+ ]
696
+
697
+ # Scale encoded video (can use standard quality)
698
+ enc_cmd = [
699
+ 'ffmpeg', '-y', '-i', encoded_file,
700
+ '-vf', filter_complex,
701
+ '-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '18',
702
+ '-pix_fmt', 'yuv420p', scaled_enc
703
+ ]
704
+
705
+ # Execute commands
706
+ ref_result = subprocess.run(ref_cmd, capture_output=True, text=True)
707
+ if ref_result.returncode != 0:
708
+ if logging_enabled:
709
+ print(f"Reference frame rate scaling failed: {ref_result.stderr}")
710
+ return None, None
711
+
712
+ enc_result = subprocess.run(enc_cmd, capture_output=True, text=True)
713
+ if enc_result.returncode != 0:
714
+ if logging_enabled:
715
+ print(f"Encoded frame rate scaling failed: {enc_result.stderr}")
716
+ return None, None
717
+
718
+ # Verify scaled files exist and have content
719
+ if (os.path.exists(scaled_ref) and os.path.getsize(scaled_ref) > 0 and
720
+ os.path.exists(scaled_enc) and os.path.getsize(scaled_enc) > 0):
721
+
722
+ if logging_enabled:
723
+ quality_note = "lossless" if is_input_raw else "high quality"
724
+ print(f"Frame rate scaling successful: {min_original_fps:.1f} → {target_fps} FPS ({quality_note} reference)")
725
+ return scaled_ref, scaled_enc
726
+ else:
727
+ if logging_enabled:
728
+ print("Frame rate scaling produced empty files")
729
+ return None, None
730
+
731
+ except Exception as e:
732
+ if logging_enabled:
733
+ print(f"Frame rate scaling error: {e}")
734
+ return None, None