shvchenko commited on
Commit
283f712
·
verified ·
1 Parent(s): fe1636e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -133
app.py CHANGED
@@ -1,143 +1,146 @@
1
  import gradio as gr
2
- import re
3
  import random
 
 
4
  import json
 
5
 
6
- # Load JSON file at startup
7
  with open("channels_fixed.json", "r", encoding="utf-8") as f:
8
- CHANNELS_JSON = json.load(f)
9
-
10
- # Hardcoded corrections and forced mappings
11
- HARDCODED_FIXES = {
12
- "BTN USA": "Big.Ten.Network.HD.us2",
13
- "SNY": "SNY.SportsNet.New.York.HD.us2",
14
- "MASN": "MASN.-.Mid.Atlantic.Sports.Network.us2",
15
- "YES": "Yes.Network.us2",
16
- "MSG": "MSG.National.us2",
17
- "REELZ USA": "ReelzChannel.HD.us2",
18
- "ABCNY USA": "ABC.(WABC).New.York,.NY.us",
19
- "OWN USA": "Oprah.Winfrey.Network.HD.us2",
20
- "DISCOVERY USA": "Discovery.Channel.ca2",
21
- "OUTDOOR CHANNEL": "Outdoor.Channel.HD.us2",
22
- "OUTDOOR CHANNEL USA": "Outdoor.Channel.HD.us2",
23
- "LOITV": "Soccer.Dummy.us",
24
- }
25
-
26
- # Function to generate random 3-letter filename
27
- def random_filename(is_upper=False):
28
- letters = ''.join(random.choices("ABCDEFGHIJKLMNOPQRSTUVWXYZ", k=3))
29
- return letters + ".m3u" if is_upper else letters.lower() + ".m3u"
30
-
31
- # Resolve tvg-id using priority
32
- def get_tvg_id(channel_name, json_data):
33
- """
34
- Priority:
35
- 1. Hardcoded fixes
36
- 2. JSON matches
37
- 3. Event special fallbacks
38
- 4. Default Live.Event.us
39
- """
40
- # Step 1: Hardcoded
41
- for key, val in HARDCODED_FIXES.items():
42
- if key.lower() in channel_name.lower():
43
- return val
44
-
45
- # Step 2: JSON lookup
46
- for json_channel, info in json_data.items():
47
- if channel_name.lower() in json_channel.lower():
48
- return info.get("tvg_id", "")
49
-
50
- # Step 3: Event fallback rules
51
- if "MLB" in channel_name.upper():
52
- return "MLB.Baseball.Dummy.us"
53
- if "NHL" in channel_name.upper():
54
- return "NHL.Hockey.Dummy.us"
55
- if "ATP" in channel_name.upper() or "WTA" in channel_name.upper():
56
- return "Tennis.Channel.us"
57
-
58
- # Step 4: Default
59
- return "Live.Event.us"
60
-
61
- # Process the M3U
62
  def process_m3u(m3u_text):
63
- lines = m3u_text.strip().splitlines()
64
-
65
- out_247, out_events = ["#EXTM3U"], ["#EXTM3U"]
66
-
67
- for i in range(len(lines)):
68
- line = lines[i]
69
-
 
 
 
 
 
 
 
 
70
  if line.startswith("#EXTINF"):
71
- # Extract channel name from EXTINF line
72
- match = re.search(r",(.*)", line)
73
- if not match:
74
- continue
75
- channel_name = match.group(1)
76
-
77
- # Decide if it's 24/7 or Events
78
- is_event = "EVENTS" in line.upper()
79
-
80
- # Resolve tvg-id
81
- tvg_id = get_tvg_id(channel_name, CHANNELS_JSON)
82
-
83
- # Ensure "test" → Live.Event.us in Events
84
- if tvg_id == "test" and is_event:
85
- tvg_id = "Live.Event.us"
86
- if tvg_id == "test" and not is_event:
87
- tvg_id = "Info.Guide.Dummy.us"
88
-
89
- # Replace or insert tvg-id
90
- if 'tvg-id="' in line:
91
- line = re.sub(r'tvg-id="[^"]*"', f'tvg-id="{tvg_id}"', line)
92
- else:
93
- line = line.replace("#EXTINF:-1", f'#EXTINF:-1 tvg-id="{tvg_id}"')
94
-
95
- # Append to correct list
96
- if is_event:
97
- out_events.append(line)
98
- if i + 1 < len(lines):
99
- out_events.append(lines[i+1])
100
- else:
101
- out_247.append(line)
102
- if i + 1 < len(lines):
103
- out_247.append(lines[i+1])
104
-
105
- # Generate random filenames
106
- filename_247 = random_filename(is_upper=True)
107
- filename_events = random_filename(is_upper=False)
108
-
109
- return (
110
- "\n".join(out_247),
111
- "\n".join(out_events),
112
- filename_247,
113
- filename_events
114
- )
115
-
116
- # Gradio UI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  def run_app(m3u_text):
118
- out_247, out_events, file_247, file_events = process_m3u(m3u_text)
119
- return out_247, out_events, (file_247, out_247), (file_events, out_events)
120
-
121
- with gr.Blocks() as demo:
122
- gr.Markdown("## 📺 M3U Playlist Processor (Project 1)")
123
-
124
- m3u_input = gr.Textbox(label="Paste M3U Playlist", lines=15, placeholder="Paste your .m3u playlist here...")
125
-
126
- with gr.Row():
127
- out_247 = gr.Textbox(label="24/7 Channels Output", lines=20)
128
- out_events = gr.Textbox(label="Events Channels Output", lines=20)
129
-
130
- with gr.Row():
131
- dl_247 = gr.File(label="Download 24/7 M3U", type="binary")
132
- dl_events = gr.File(label="Download Events M3U", type="binary")
133
-
134
- process_btn = gr.Button("Process Playlist")
135
-
136
- process_btn.click(
137
- fn=run_app,
138
- inputs=[m3u_input],
139
- outputs=[out_247, out_events, dl_247, dl_events]
140
- )
141
 
142
  if __name__ == "__main__":
143
- demo.launch()
 
1
  import gradio as gr
 
2
  import random
3
+ import string
4
+ import re
5
  import json
6
+ from difflib import get_close_matches
7
 
8
+ # Load channel mapping JSON
9
  with open("channels_fixed.json", "r", encoding="utf-8") as f:
10
+ channel_map = json.load(f)
11
+
12
+ # Random 3-character filename generator
13
+ def random_filename(uppercase=True):
14
+ letters = string.ascii_uppercase if uppercase else string.ascii_lowercase
15
+ return "".join(random.choice(letters) for _ in range(3)) + ".m3u"
16
+
17
+ # Insert tvg-id immediately after #EXTINF:-1
18
+ def insert_tvg_id(extinf_line, tvg_id):
19
+ # Remove existing tvg-id if present
20
+ line = re.sub(r'tvg-id="[^"]*"', '', extinf_line)
21
+ # Insert tvg-id immediately after #EXTINF:-1
22
+ line = line.replace('#EXTINF:-1', f'#EXTINF:-1 tvg-id="{tvg_id}"', 1)
23
+ # Clean multiple spaces
24
+ line = re.sub(r'\s+', ' ', line).strip()
25
+ return line
26
+
27
+ # Apply special event rules
28
+ def apply_event_special_cases(extinf_line, channel_name):
29
+ line = extinf_line
30
+ if "MLB LEAGUE PASS" in channel_name:
31
+ return insert_tvg_id(line, "MLB.Baseball.Dummy.us")
32
+ if "NHL GAMECENTER" in channel_name:
33
+ return insert_tvg_id(line, "NHL.Hockey.Dummy.us")
34
+ if "ATP" in channel_name or "WTA" in channel_name:
35
+ return insert_tvg_id(line, "Tennis.Channel.us")
36
+ if 'tvg-id="test"' in line:
37
+ return insert_tvg_id(line, "Live.Event.us")
38
+ return line
39
+
40
+ # Main processor
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  def process_m3u(m3u_text):
42
+ lines = m3u_text.splitlines()
43
+ out_247_blocks = []
44
+ out_events_blocks = []
45
+ log = []
46
+
47
+ # Hardcoded abbreviation exceptions for Events
48
+ exceptions = {
49
+ "BTN": "Big.Ten.Network.HD.us2",
50
+ "SNY": "SNY.SportsNet.New.York.HD.us2",
51
+ "MASN": "MASN.-.Mid.Atlantic.Sports.Network.us2",
52
+ "YES": "Yes.Network.us2",
53
+ "MSG": "MSG.National.us2",
54
+ }
55
+
56
+ for i, line in enumerate(lines):
57
  if line.startswith("#EXTINF"):
58
+ url = lines[i+1] if i+1 < len(lines) else ""
59
+ extinf = line
60
+
61
+ # Extract tvg-id and channel name
62
+ tvg_match = re.search(r'tvg-id="([^"]*)"', extinf)
63
+ tvg_id = tvg_match.group(1) if tvg_match else None
64
+ name_match = re.search(r",(.*)", extinf)
65
+ channel_name = name_match.group(1).strip() if name_match else ""
66
+
67
+ # Extract group
68
+ group_match = re.search(r'group-title="([^"]*)"', extinf)
69
+ group_title = group_match.group(1).upper().strip() if group_match else ""
70
+
71
+ # --- 24/7 CHANNELS ---
72
+ if group_title and "24/7" in group_title:
73
+ if tvg_id == "test" or not tvg_id:
74
+ new_ext = insert_tvg_id(extinf, "Info.Guide.Dummy.us")
75
+ log.append(f"ℹ️ 24/7 | {channel_name} → Info.Guide.Dummy.us")
76
+ else:
77
+ new_ext = extinf
78
+ out_247_blocks.append((new_ext, url))
79
+
80
+ # --- EVENTS ---
81
+ elif group_title and "EVENTS" in group_title:
82
+ candidate = channel_name
83
+
84
+ # Check hardcoded abbreviation exceptions
85
+ matched_exception = False
86
+ for abbr, fixed_id in exceptions.items():
87
+ if candidate.upper().startswith(abbr):
88
+ new_ext = insert_tvg_id(extinf, fixed_id)
89
+ new_ext = apply_event_special_cases(new_ext, candidate)
90
+ out_events_blocks.append((new_ext, url))
91
+ log.append(f"✅ EVENTS | {candidate} → {fixed_id} (hardcoded {abbr} exception)")
92
+ matched_exception = True
93
+ break
94
+ if matched_exception:
95
+ continue
96
+
97
+ # Try matching against JSON
98
+ match = None
99
+ for key, data in channel_map.items():
100
+ if data.get("tvg_id") and key.lower() in candidate.lower():
101
+ match = data["tvg_id"]
102
+ break
103
+ if not match:
104
+ keys = list(channel_map.keys())
105
+ guesses = get_close_matches(candidate, keys, n=1, cutoff=0.6)
106
+ if guesses:
107
+ match = channel_map[guesses[0]].get("tvg_id")
108
+
109
+ if match:
110
+ new_ext = insert_tvg_id(extinf, match)
111
+ log.append(f"✅ EVENTS | {candidate} → {match}")
112
+ else:
113
+ new_ext = insert_tvg_id(extinf, "Live.Event.us")
114
+ log.append(f"⚠️ EVENTS | {candidate} → Live.Event.us (fallback)")
115
+
116
+ new_ext = apply_event_special_cases(new_ext, candidate)
117
+ out_events_blocks.append((new_ext, url))
118
+
119
+ # Build output playlists
120
+ out_247 = "\n".join([f"{ext}\n{url}" for ext, url in out_247_blocks])
121
+ out_events = "\n".join([f"{ext}\n{url}" for ext, url in out_events_blocks])
122
+
123
+ return out_247, out_events, "\n".join(log)
124
+
125
+ # Gradio UI with file downloads
126
  def run_app(m3u_text):
127
+ out_247, out_events, log = process_m3u(m3u_text)
128
+ file_247 = random_filename(uppercase=True)
129
+ file_events = random_filename(uppercase=False)
130
+ with open(file_247, "w", encoding="utf-8") as f:
131
+ f.write(out_247)
132
+ with open(file_events, "w", encoding="utf-8") as f:
133
+ f.write(out_events)
134
+
135
+ return file_247, file_events, log
136
+
137
+ iface = gr.Interface(
138
+ fn=run_app,
139
+ inputs=gr.Textbox(lines=15, placeholder="Paste your M3U playlist here..."),
140
+ outputs=[gr.File(label="24/7 Playlist"), gr.File(label="Events Playlist"), gr.Textbox(label="Log")],
141
+ title="Project 1 Playlist Processor",
142
+ description="Splits 24/7 and Events playlists, applies JSON tvg-id mappings, adds special sports rules, and outputs two clean M3Us."
143
+ )
 
 
 
 
 
 
144
 
145
  if __name__ == "__main__":
146
+ iface.launch()