Alide21 commited on
Commit
81bd079
·
verified ·
1 Parent(s): c3109de

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +55 -199
main.py CHANGED
@@ -12,14 +12,14 @@ TARGET_DATE = datetime(2026, 2, 19, 0, 0, 0)
12
  STREAM_KEY = os.environ.get("STREAM_KEY")
13
  STREAM_URL = "rtmp://a.rtmp.youtube.com/live2/"
14
  BG_IMAGE = "https://images.unsplash.com/photo-1555400038-63f5ba517a47?q=80&w=1920&auto=format&fit=crop"
15
- # ==========================================
16
 
17
  app = Flask(__name__)
18
 
19
  @app.route('/time_data')
20
  def time_data():
21
- """نقطة نهاية (API) لتقديم بيانات الوقت المتبقي كـ JSON"""
22
  diff = TARGET_DATE - datetime.now()
 
 
23
  days = diff.days
24
  hours, rem = divmod(diff.seconds, 3600)
25
  minutes, seconds = divmod(rem, 60)
@@ -32,236 +32,92 @@ def time_data():
32
 
33
  @app.route('/')
34
  def home():
35
- # واجهة المحاكاة المطورة باستخدام HTML/CSS/JS
36
- return f"""
37
  <!DOCTYPE html>
38
  <html lang="ar" dir="rtl">
39
  <head>
40
  <meta charset="UTF-8">
41
- <title>بث مباشر | يوتيوب</title>
42
- <link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;700;900&display=swap" rel="stylesheet">
43
  <style>
44
- /* الأساسيات */
45
- body {{ background-color: #181818; color: white; font-family: 'Cairo', sans-serif; margin: 0; display: flex; justify-content: center; }}
46
- .container {{ width: 90%; max-width: 1200px; margin-top: 20px; }}
47
-
48
- /* مشغل الفيديو مع حل مشكلة الوميض (الصورة ثابتة في الخلفية) */
49
- .video-player {{
50
- position: relative;
51
- width: 100%;
52
- aspect-ratio: 16/9;
53
- background: url('{BG_IMAGE}') no-repeat center;
54
- background-size: cover;
55
- box-shadow: 0 10px 30px rgba(0,0,0,0.5);
56
- display: flex;
57
- flex-direction: column;
58
- justify-content: center;
59
- align-items: center;
60
- overflow: hidden;
61
- }}
62
- .overlay {{ position: absolute; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1; }}
63
- .content-wrapper {{ z-index: 2; text-align: center; }}
64
- .main-title {{ font-size: 3vw; font-weight: 900; margin-bottom: 30px; color: #fff; text-shadow: 0 2px 5px rgba(0,0,0,0.5); }}
65
-
66
- /* --- تصميم العداد القلاب الواقعي (Flip Clock) --- */
67
- .countdown-row {{ display: flex; gap: 20px; justify-content: center; direction: ltr; }}
68
- .time-unit {{ display: flex; flex-direction: column; align-items: center; }}
69
 
70
- .flip-card {{
71
- position: relative;
72
- width: 7vw;
73
- height: 9vw;
74
- perspective: 1000px; /* عمق للحركة ثلاثية الأبعاد */
75
- font-size: 5vw;
76
- font-weight: bold;
77
- color: white;
78
- box-shadow: 0 4px 10px rgba(0,0,0,0.3);
79
- border-radius: 8px;
80
- }}
81
-
82
- /* الجزء العلوي والسفلي من البطاقة */
83
- .top, .bottom, .top-flip, .bottom-flip {{
84
- position: absolute;
85
- width: 100%;
86
- height: 50%;
87
- overflow: hidden;
88
- backface-visibility: hidden; /* إخفاء الظهر عند القلب */
89
- background: #4CAF50; /* لون العداد الأخضر */
90
- display: flex;
91
- justify-content: center;
92
- }}
93
-
94
- /* تنسيق الجزء العلوي */
95
- .top, .top-flip {{
96
- top: 0;
97
- border-top-left-radius: 8px;
98
- border-top-right-radius: 8px;
99
- align-items: flex-end;
100
- border-bottom: 1px solid rgba(0,0,0,0.1); /* خط المنتصف */
101
- background: linear-gradient(180deg, #5DBE62 0%, #4CAF50 100%);
102
- }}
103
-
104
- /* تنسيق الجزء السفلي */
105
- .bottom, .bottom-flip {{
106
- bottom: 0;
107
- border-bottom-left-radius: 8px;
108
- border-bottom-right-radius: 8px;
109
- align-items: flex-start;
110
- background: linear-gradient(180deg, #4CAF50 0%, #388E3C 100%);
111
- }}
112
-
113
- /* ضبط موقع الرقم داخل النصفين */
114
- .top span, .top-flip span {{ transform: translateY(50%); }}
115
- .bottom span, .bottom-flip span {{ transform: translateY(-50%); }}
116
-
117
- /* --- حركات القلب (Animations) --- */
118
- .top-flip {{
119
- transform-origin: bottom;
120
- animation: flip-top 0.6s ease-in forwards;
121
- z-index: 2; /* يظهر فوق الجزء السفلي أثناء الحركة */
122
- }}
123
- .bottom-flip {{
124
- transform-origin: top;
125
- transform: rotateX(90deg); /* يبدأ مخفياً */
126
- animation: flip-bottom 0.6s ease-out 0.6s forwards; /* يبدأ بعد انتهاء النصف الأول */
127
- }}
128
-
129
- @keyframes flip-top {{
130
- 100% {{ transform: rotateX(-90deg); }}
131
- }}
132
- @keyframes flip-bottom {{
133
- 100% {{ transform: rotateX(0deg); }}
134
- }}
135
-
136
- .label {{ margin-top: 15px; font-size: 1.5vw; color: #ddd; font-weight: bold; }}
137
- .video-info {{ margin-top: 15px; border-bottom: 1px solid #303030; padding-bottom: 15px; }}
138
- .vid-title {{ font-size: 22px; font-weight: bold; }}
139
- .meta {{ color: #aaa; font-size: 14px; margin-top: 5px; }}
140
  </style>
141
  </head>
142
  <body>
143
- <div class="container">
144
- <div class="video-player">
145
- <div class="overlay"></div>
146
- <div class="content-wrapper">
147
- <div class="main-title">كم باقي على رمضان 2026</div>
148
- <div class="countdown-row">
149
- <div class="time-unit" id="days-unit"></div>
150
- <div class="time-unit" id="hours-unit"></div>
151
- <div class="time-unit" id="minutes-unit"></div>
152
- <div class="time-unit" id="seconds-unit"></div>
153
- </div>
154
  </div>
155
  </div>
156
- <div class="video-info">
157
- <div class="vid-title">بث مباشر 🔴 العد التنازلي (تصميم Flip واقعي)</div>
158
- <div class="meta">من كل صوب • تحديث لحظي • بدون وميض</div>
159
- </div>
160
  </div>
161
-
162
  <script>
163
- // دالة لإنشاء هيكل بطاقة Flip
164
- function createFlipCard(initialValue) {
165
- const card = document.createElement('div');
166
- card.className = 'flip-card';
167
- card.innerHTML = `
168
- <div class="top"><span>${initialValue}</span></div>
169
- <div class="bottom"><span>${initialValue}</span></div>
170
- `;
171
- return card;
172
- }
173
-
174
- // دالة لتحديث البطاقة وإضافة حركة القلب إذا تغيرت القيمة
175
- function updateCard(unitId, newValue) {
176
- const unitElement = document.getElementById(unitId);
177
- let card = unitElement.querySelector('.flip-card');
178
-
179
- // إذا كانت البطاقة غير موجودة، قم بإنشائها
180
- if (!card) {
181
- card = createFlipCard(newValue);
182
- const label = document.createElement('div');
183
- label.className = 'label';
184
- // تحديد التسمية الصحيحة
185
- const labels = { 'days-unit': 'أيام', 'hours-unit': 'ساعات', 'minutes-unit': 'دقائق', 'seconds-unit': 'ثواني' };
186
- label.textContent = labels[unitId];
187
- unitElement.appendChild(card);
188
- unitElement.appendChild(label);
189
- return;
190
- }
191
-
192
- const currentValue = card.querySelector('.top span').textContent;
193
- // تحديث فقط إذا تغيرت القيمة لتشغيل الحركة
194
- if (newValue !== currentValue) {
195
- // إضافة عناصر الحركة (التي ستقوم بالانقلاب)
196
- const topFlip = document.createElement('div');
197
- topFlip.className = 'top-flip';
198
- topFlip.innerHTML = `<span>${currentValue}</span>`;
199
-
200
- const bottomFlip = document.createElement('div');
201
- bottomFlip.className = 'bottom-flip';
202
- bottomFlip.innerHTML = `<span>${newValue}</span>`;
203
-
204
- card.appendChild(topFlip);
205
- card.appendChild(bottomFlip);
206
-
207
- // تحديث القيم الثابتة بعد انتهاء الحركة
208
- setTimeout(() => {
209
- card.querySelector('.top span').textContent = newValue;
210
- card.querySelector('.bottom span').textContent = newValue;
211
- card.removeChild(topFlip);
212
- card.removeChild(bottomFlip);
213
- }, 1200); // مدة الحركة الإجمالية (0.6s + 0.6s)
214
- }
215
  }
216
-
217
- // جلب البيانات وتحديث العداد كل ثانية
218
- function updateCountdown() {
219
- fetch('/time_data')
220
- .then(response => response.json())
221
- .then(data => {
222
- updateCard('days-unit', data.days);
223
- updateCard('hours-unit', data.hours);
224
- updateCard('minutes-unit', data.minutes);
225
- updateCard('seconds-unit', data.seconds);
226
- });
227
- }
228
-
229
- // تشغيل التحديث لأول مرة ثم كل 1000ms
230
- updateCountdown();
231
- setInterval(updateCountdown, 1000);
232
  </script>
233
  </body>
234
  </html>
235
  """
 
236
 
237
  def start_stream():
238
  if not STREAM_KEY: return
239
  target_ts = int(TARGET_DATE.timestamp())
240
 
241
- # ملاحظة هامة: تأثير القلب الواقعي جداً (Animation) لا يمكن تنفيذه
242
- # في البث المباشر باستخدام FFmpeg وحده لأنه يتطلب محرك رسم متقدم.
243
- # الكود أدناه سيبث عداداً ثابتاً بخلفية خضراء كما في النسخة السابقة،
244
- # وهو أفضل ما يمكن تقديمه تقنياً للبث المستمر.
245
  ffmpeg_cmd = [
246
  'ffmpeg', '-re', '-loop', '1', '-i', BG_IMAGE,
247
  '-f', 'lavfi', '-i', 'anullsrc',
248
  '-vf', (
249
- f"scale=1280:720,"
250
- f"drawbox=x=0:y=0:w=iw:h=ih:color=black@0.4:t=fill," # تعتيم للخلفية
251
- f"drawtext=text='Ramadan 2026':x=(w-text_w)/2:y=150:fontsize=70:fontcolor=white:shadowcolor=black:shadowx=2:shadowy=2,"
252
- # العداد بخلفية خضراء ثابتة للبث
253
- f"drawtext=text=' %{{pts\\:localtime\\:{target_ts}\\:%d}} : %{{pts\\:localtime\\:{target_ts}\\:%H}} : %{{pts\\:localtime\\:{target_ts}\\:%M}} : %{{pts\\:localtime\\:{target_ts}\\:%S}} ':"
254
- f"x=(w-text_w)/2:y=(h-text_h)/2+20:fontsize=90:fontcolor=white:box=1:boxcolor=#4CAF50@0.9:boxborderw=20"
255
  ),
256
- '-vcodec', 'libx264', '-preset', 'veryfast', '-b:v', '3000k', '-maxrate', '3000k', '-bufsize', '6000k',
257
  '-pix_fmt', 'yuv420p', '-g', '60', '-f', 'flv',
258
  f"{STREAM_URL}{STREAM_KEY}"
259
  ]
260
- subprocess.run(ffmpeg_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
261
 
262
  if __name__ == "__main__":
263
  threading.Thread(target=lambda: app.run(host='0.0.0.0', port=7860), daemon=True).start()
264
- print("Real Flip Clock Simulation Started (No Flicker)...")
265
  while True:
266
  try: start_stream()
267
- except: time.sleep(10)
 
12
  STREAM_KEY = os.environ.get("STREAM_KEY")
13
  STREAM_URL = "rtmp://a.rtmp.youtube.com/live2/"
14
  BG_IMAGE = "https://images.unsplash.com/photo-1555400038-63f5ba517a47?q=80&w=1920&auto=format&fit=crop"
 
15
 
16
  app = Flask(__name__)
17
 
18
  @app.route('/time_data')
19
  def time_data():
 
20
  diff = TARGET_DATE - datetime.now()
21
+ if diff.total_seconds() < 0:
22
+ return jsonify({'days': '00', 'hours': '00', 'minutes': '00', 'seconds': '00'})
23
  days = diff.days
24
  hours, rem = divmod(diff.seconds, 3600)
25
  minutes, seconds = divmod(rem, 60)
 
32
 
33
  @app.route('/')
34
  def home():
35
+ # استخدمنا رندر مباشر للنص لتجنب مشاكل f-string مع JavaScript
36
+ html_content = """
37
  <!DOCTYPE html>
38
  <html lang="ar" dir="rtl">
39
  <head>
40
  <meta charset="UTF-8">
41
+ <title>بث مباشر | رمضان 2026</title>
42
+ <link href="https://fonts.googleapis.com/css2?family=Cairo:wght@700;900&display=swap" rel="stylesheet">
43
  <style>
44
+ body { background-color: #0f0f0f; color: white; font-family: 'Cairo', sans-serif; margin: 0; overflow: hidden; }
45
+ .video-player {
46
+ position: relative; width: 100vw; height: 100vh;
47
+ background: url('""" + BG_IMAGE + """') no-repeat center;
48
+ background-size: cover; display: flex; flex-direction: column; justify-content: center; align-items: center;
49
+ }
50
+ .overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); }
51
+ .content { z-index: 10; text-align: center; }
52
+ .title { font-size: 45px; margin-bottom: 30px; text-shadow: 2px 2px 10px rgba(0,0,0,0.8); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
+ .countdown { display: flex; gap: 20px; direction: ltr; }
55
+ .unit { display: flex; flex-direction: column; align-items: center; }
56
+ .card {
57
+ background: linear-gradient(180deg, #4CAF50, #2E7D32);
58
+ width: 120px; height: 160px; border-radius: 12px;
59
+ font-size: 80px; font-weight: 900; display: flex;
60
+ justify-content: center; align-items: center;
61
+ box-shadow: 0 10px 20px rgba(0,0,0,0.4);
62
+ border: 1px solid rgba(255,255,255,0.1);
63
+ }
64
+ .label { margin-top: 10px; font-size: 20px; color: #4CAF50; font-weight: bold; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  </style>
66
  </head>
67
  <body>
68
+ <div class="video-player">
69
+ <div class="overlay"></div>
70
+ <div class="content">
71
+ <div class="title">الوقت المتبقي على رمضان 2026</div>
72
+ <div class="countdown">
73
+ <div class="unit"><div class="card" id="days">00</div><div class="label">أيام</div></div>
74
+ <div class="unit"><div class="card" id="hours">00</div><div class="label">ساعة</div></div>
75
+ <div class="unit"><div class="card" id="minutes">00</div><div class="label">دقيقة</div></div>
76
+ <div class="unit"><div class="card" id="seconds">00</div><div class="label">ثانية</div></div>
 
 
77
  </div>
78
  </div>
 
 
 
 
79
  </div>
 
80
  <script>
81
+ function update() {
82
+ fetch('/time_data').then(res => res.json()).then(data => {
83
+ document.getElementById('days').innerText = data.days;
84
+ document.getElementById('hours').innerText = data.hours;
85
+ document.getElementById('minutes').innerText = data.minutes;
86
+ document.getElementById('seconds').innerText = data.seconds;
87
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
+ setInterval(update, 1000);
90
+ update();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  </script>
92
  </body>
93
  </html>
94
  """
95
+ return html_content
96
 
97
  def start_stream():
98
  if not STREAM_KEY: return
99
  target_ts = int(TARGET_DATE.timestamp())
100
 
101
+ # تحسين جودة فيديو اليوتيوب ليكون مشابهاً للتصميم الأخضر
 
 
 
102
  ffmpeg_cmd = [
103
  'ffmpeg', '-re', '-loop', '1', '-i', BG_IMAGE,
104
  '-f', 'lavfi', '-i', 'anullsrc',
105
  '-vf', (
106
+ f"scale=1280:720,setsar=1,"
107
+ f"drawbox=y=ih/2-100:color=black@0.6:width=iw:height=250:t=fill," # شريط خلفية للعداد
108
+ f"drawtext=text='Ramadan 2026':x=(w-text_w)/2:y=200:fontsize=60:fontcolor=white,"
109
+ # العداد الأخضر في البث
110
+ f"drawtext=text=' %{{pts\\:localtime\\:{target_ts}\\:%d}} %{{pts\\:localtime\\:{target_ts}\\:%H}} %{{pts\\:localtime\\:{target_ts}\\:%M}} %{{pts\\:localtime\\:{target_ts}\\:%S}} ':"
111
+ f"x=(w-text_w)/2:y=(h-text_h)/2+30:fontsize=100:fontcolor=white:box=1:boxcolor=#4CAF50@1:boxborderw=30"
112
  ),
113
+ '-vcodec', 'libx264', '-preset', 'veryfast', '-b:v', '3500k', '-maxrate', '3500k', '-bufsize', '7000k',
114
  '-pix_fmt', 'yuv420p', '-g', '60', '-f', 'flv',
115
  f"{STREAM_URL}{STREAM_KEY}"
116
  ]
117
+ subprocess.run(ffmpeg_cmd)
118
 
119
  if __name__ == "__main__":
120
  threading.Thread(target=lambda: app.run(host='0.0.0.0', port=7860), daemon=True).start()
 
121
  while True:
122
  try: start_stream()
123
+ except: time.sleep(5)