shvchenko commited on
Commit
fe1636e
·
verified ·
1 Parent(s): 6176a03

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +133 -144
app.py CHANGED
@@ -1,154 +1,143 @@
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
- "NESN": "New.England.Sports.Network.HD.us2",
55
- "REELZ USA": "ReelzChannel.HD.us2",
56
- "ABCNY USA": "ABC.(WABC).New.York,.NY.us",
57
- "OWN USA": "Oprah.Winfrey.Network.HD.us2",
58
- "DISCOVERY USA": "Discovery.Channel.ca2",
59
- "OUTDOOR CHANNEL": "Outdoor.Channel.HD.us2",
60
- "OUTDOOR CHANNEL USA": "Outdoor.Channel.HD.us2",
61
- "LOITV": "Soccer.Dummy.us",
62
- }
63
-
64
- for i, line in enumerate(lines):
65
  if line.startswith("#EXTINF"):
66
- url = lines[i+1] if i+1 < len(lines) else ""
67
- extinf = line
68
-
69
- # Extract tvg-id and channel name
70
- tvg_match = re.search(r'tvg-id="([^"]*)"', extinf)
71
- tvg_id = tvg_match.group(1) if tvg_match else None
72
- name_match = re.search(r",(.*)", extinf)
73
- channel_name = name_match.group(1).strip() if name_match else ""
74
-
75
- # Extract group
76
- group_match = re.search(r'group-title="([^"]*)"', extinf)
77
- group_title = group_match.group(1).upper().strip() if group_match else ""
78
-
79
- # --- 24/7 CHANNELS ---
80
- if group_title and "24/7" in group_title:
81
- if tvg_id == "test" or not tvg_id:
82
- new_ext = insert_tvg_id(extinf, "Info.Guide.Dummy.us")
83
- log.append(f"ℹ️ 24/7 | {channel_name} → Info.Guide.Dummy.us")
84
- else:
85
- new_ext = extinf
86
- out_247_blocks.append((new_ext, url))
87
-
88
- # --- EVENTS ---
89
- elif group_title and "EVENTS" in group_title:
90
- candidate = channel_name
91
-
92
- # Check hardcoded abbreviation exceptions
93
- matched_exception = False
94
- for abbr, fixed_id in exceptions.items():
95
- if candidate.upper().startswith(abbr):
96
- new_ext = insert_tvg_id(extinf, fixed_id)
97
- new_ext = apply_event_special_cases(new_ext, candidate)
98
- out_events_blocks.append((new_ext, url))
99
- log.append(f"✅ EVENTS | {candidate} → {fixed_id} (hardcoded {abbr} exception)")
100
- matched_exception = True
101
- break
102
- if matched_exception:
103
- continue
104
-
105
- # Try matching against JSON
106
- match = None
107
- for key, data in channel_map.items():
108
- if data.get("tvg_id") and key.lower() in candidate.lower():
109
- match = data["tvg_id"]
110
- break
111
- if not match:
112
- keys = list(channel_map.keys())
113
- guesses = get_close_matches(candidate, keys, n=1, cutoff=0.6)
114
- if guesses:
115
- match = channel_map[guesses[0]].get("tvg_id")
116
-
117
- if match:
118
- new_ext = insert_tvg_id(extinf, match)
119
- log.append(f"✅ EVENTS | {candidate} → {match}")
120
- else:
121
- new_ext = insert_tvg_id(extinf, "Live.Event.us")
122
- log.append(f"⚠️ EVENTS | {candidate} → Live.Event.us (fallback)")
123
-
124
- new_ext = apply_event_special_cases(new_ext, candidate)
125
- out_events_blocks.append((new_ext, url))
126
-
127
- # Build output playlists
128
- out_247 = "\n".join([f"{ext}\n{url}" for ext, url in out_247_blocks])
129
- out_events = "\n".join([f"{ext}\n{url}" for ext, url in out_events_blocks])
130
-
131
- return out_247, out_events, "\n".join(log)
132
-
133
- # Gradio UI with file downloads
134
  def run_app(m3u_text):
135
- out_247, out_events, log = process_m3u(m3u_text)
136
- file_247 = random_filename(uppercase=True)
137
- file_events = random_filename(uppercase=False)
138
- with open(file_247, "w", encoding="utf-8") as f:
139
- f.write(out_247)
140
- with open(file_events, "w", encoding="utf-8") as f:
141
- f.write(out_events)
142
-
143
- return file_247, file_events, log
144
-
145
- iface = gr.Interface(
146
- fn=run_app,
147
- inputs=gr.Textbox(lines=15, placeholder="Paste your M3U playlist here..."),
148
- outputs=[gr.File(label="24/7 Playlist"), gr.File(label="Events Playlist"), gr.Textbox(label="Log")],
149
- title="Project 1 Playlist Processor",
150
- description="Splits 24/7 and Events playlists, applies JSON tvg-id mappings, adds special sports rules, and outputs two clean M3Us."
151
- )
 
 
 
 
 
 
152
 
153
  if __name__ == "__main__":
154
- iface.launch()
 
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()