CMS for the late garbage.fm
1<?php
2
3class Episode extends ActiveRecord\Model {
4 static $validates_presence_of = array(
5 array("episode"),
6 array("title"),
7 array("notes"),
8 array("duration"),
9 array("air_date"),
10 );
11 static $validates_uniqueness_of = array(
12 array("episode"),
13 );
14
15 static $attr_accessible = array(
16 "episode", "is_pending", "air_date", "title", "is_explicit",
17 "duration", "notes", "summary", "custom_artwork_url",
18 "custom_author",
19 );
20
21 static $after_destroy = array("delete_mp3_file");
22
23 static $after_save = array("possibly_update_chapters");
24
25 public $needs_chapter_updating;
26
27 static public function next_episode() {
28 $sth = Episode::query("SELECT MAX(episode) FROM episodes");
29 $row = $sth->fetch(PDO::FETCH_NUM);
30 $max = intval($row[0]);
31 return $max + 1;
32 }
33
34 public function get_file_path() {
35 return "episodes/garbage" . intval($this->episode) . ".mp3";
36 }
37
38 public function get_mp3_url() {
39 return Settings::fetch()->url . $this->file_path;
40 }
41
42 public function get_secure_mp3_url() {
43 return Settings::fetch()->secure_url . $this->file_path;
44 }
45
46 public function get_mp3_path() {
47 return HALFMOON_ROOT . "/public/" . $this->file_path;
48 }
49
50 /* publicly viewable page, not mp3 */
51 public function get_absolute_url() {
52 return Settings::fetch()->url . "episodes/" . $this->episode;
53 }
54
55 public function get_guid() {
56 /* return http for legacy reasons */
57 if ($this->is_pending)
58 return preg_replace("/https:/", "http:", $this->absolute_url) .
59 "?" . $this->updated_at->getTimestamp();
60 else
61 return preg_replace("/https:/", "http:", $this->absolute_url);
62 }
63
64 public function get_artwork_url() {
65 if ($this->custom_artwork_url)
66 return $this->custom_artwork_url;
67 else
68 return Settings::fetch()->logo_url;
69 }
70
71 public function get_is_comment() {
72 return !!(preg_match("/\/* .* *\//", $this->title));
73 }
74
75 public function get_full_title() {
76 $t = "";
77
78 if (!$this->is_comment)
79 $t .= "garbage[" . $this->episode . "]: ";
80
81 $t .= $this->title;
82
83 if ($this->is_pending)
84 $t .= " [PENDING]";
85
86 return $t;
87 }
88
89 public function get_secure_artwork_url() {
90 if ($this->custom_artwork_url)
91 return preg_replace("/^http:/i", "https:",
92 $this->custom_artwork_url);
93 else
94 return Settings::fetch()->secure_logo_url;
95 }
96
97 public function possibly_update_chapters() {
98 if ($this->needs_chapter_updating && file_exists($this->mp3_path) &&
99 count($this->chapters)) {
100 $args = "/garbage.fm/bin/mp3chap " .
101 escapeshellarg($this->mp3_path);
102
103 foreach ($this->chapters as $time => $content) {
104 $secs = 0;
105
106 if (preg_match("/^(\d+):(\d+):(\d+)$/", $time, $m))
107 $secs = intval($m[3]) + (intval($m[2]) * 60) +
108 (intval($m[1]) * 60 * 60);
109 elseif (preg_match("/^(\d+):(\d+)$/", $time, $m))
110 $secs = intval($m[2]) + (intval($m[1]) * 60);
111 else
112 $secs = intval($time);
113
114 $args .= " " . $secs . " " . escapeshellarg($content);
115 }
116
117 error_log("Running mp3chap: " . $args);
118 system($args);
119 } else {
120 $this->needs_chapter_updating = false;
121 }
122 }
123
124 public function set_notes($notes) {
125 $old_notes = $this->notes;
126 $this->assign_attribute("notes", $notes);
127
128 $parsedown = new Parsedown();
129 $this->notes_html = $parsedown->text($notes);
130
131 if ($old_notes != $notes)
132 $this->needs_chapter_updating = true;
133 }
134
135 public function set_duration($duration) {
136 $secs = 0;
137
138 if (preg_match("/^(\d+):(\d+):(\d+)$/", $duration, $m))
139 $secs = intval($m[3]) + (intval($m[2]) * 60) +
140 (intval($m[1]) * 60 * 60);
141 elseif (preg_match("/^(\d+):(\d+)$/", $duration, $m))
142 $secs = intval($m[2]) + (intval($m[1]) * 60);
143 else
144 $secs = intval($duration);
145
146 $this->assign_attribute("duration_secs", $secs);
147
148 $hours = floor($secs / 3600);
149 $mins = floor(($secs - ($hours * 3600)) / 60);
150 $secs = floor($secs % 60);
151
152 $tms = "";
153 if ($hours > 0)
154 $tms = sprintf("%d:%02d:%02d", $hours, $mins, $secs);
155 else
156 $tms = sprintf("%d:%02d", $mins, $secs);
157
158 $this->assign_attribute("duration", $tms);
159 }
160
161 public function get_chapters() {
162 $chapters = array();
163
164 $lines = explode("\n", $this->notes_html);
165 foreach ($lines as $line) {
166 if (preg_match("/^(<p>)?(.+?) <!-- ([0-9:]+) -->/", $line, $m))
167 $chapters[$m[3]] = html_entity_decode(strip_tags($m[2]));
168 }
169
170 return $chapters;
171 }
172
173 public function take_new_mp3($file) {
174 move_uploaded_file($file, $this->mp3_path);
175 $this->filesize = filesize($this->mp3_path);
176 $this->needs_chapter_updating = true;
177 }
178
179 public function delete_mp3_file() {
180 unlink($this->mp3_path);
181 }
182}
183
184?>