mirror of
				https://github.com/bspeice/elektricity
				synced 2025-10-31 17:20:23 -04:00 
			
		
		
		
	Initial repod server commit with basic Bassdrive support
This commit is contained in:
		
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | *.pyc | ||||||
|  | bin/ | ||||||
|  | lib/ | ||||||
|  | share/ | ||||||
|  | include/ | ||||||
|  | .idea/workspace.xml | ||||||
							
								
								
									
										1
									
								
								.idea/.name
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.idea/.name
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | repod | ||||||
							
								
								
									
										22
									
								
								.idea/compiler.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								.idea/compiler.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="CompilerConfiguration"> | ||||||
|  |     <resourceExtensions /> | ||||||
|  |     <wildcardResourcePatterns> | ||||||
|  |       <entry name="!?*.java" /> | ||||||
|  |       <entry name="!?*.form" /> | ||||||
|  |       <entry name="!?*.class" /> | ||||||
|  |       <entry name="!?*.groovy" /> | ||||||
|  |       <entry name="!?*.scala" /> | ||||||
|  |       <entry name="!?*.flex" /> | ||||||
|  |       <entry name="!?*.kt" /> | ||||||
|  |       <entry name="!?*.clj" /> | ||||||
|  |       <entry name="!?*.aj" /> | ||||||
|  |     </wildcardResourcePatterns> | ||||||
|  |     <annotationProcessing> | ||||||
|  |       <profile default="true" name="Default" enabled="false"> | ||||||
|  |         <processorPath useClasspath="true" /> | ||||||
|  |       </profile> | ||||||
|  |     </annotationProcessing> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										3
									
								
								.idea/copyright/profiles_settings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.idea/copyright/profiles_settings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | <component name="CopyrightManager"> | ||||||
|  |   <settings default="" /> | ||||||
|  | </component> | ||||||
							
								
								
									
										6
									
								
								.idea/encodings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/encodings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="Encoding"> | ||||||
|  |     <file url="PROJECT" charset="UTF-8" /> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										35
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="FrameworkDetectionExcludesConfiguration"> | ||||||
|  |     <type id="django" /> | ||||||
|  |   </component> | ||||||
|  |   <component name="ProjectLevelVcsManager" settingsEditedManually="false"> | ||||||
|  |     <OptionsSetting value="true" id="Add" /> | ||||||
|  |     <OptionsSetting value="true" id="Remove" /> | ||||||
|  |     <OptionsSetting value="true" id="Checkout" /> | ||||||
|  |     <OptionsSetting value="true" id="Update" /> | ||||||
|  |     <OptionsSetting value="true" id="Status" /> | ||||||
|  |     <OptionsSetting value="true" id="Edit" /> | ||||||
|  |     <ConfirmationsSetting value="0" id="Add" /> | ||||||
|  |     <ConfirmationsSetting value="0" id="Remove" /> | ||||||
|  |   </component> | ||||||
|  |   <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="Python 3.5.1 (C:\Users\Bradlee Speice\Anaconda3\python.exe)" project-jdk-type="Python SDK"> | ||||||
|  |     <output url="file://$PROJECT_DIR$/out" /> | ||||||
|  |   </component> | ||||||
|  |   <component name="masterDetails"> | ||||||
|  |     <states> | ||||||
|  |       <state key="ProjectJDKs.UI"> | ||||||
|  |         <settings> | ||||||
|  |           <last-edited>Python 3.5.1 (C:\Users\Bradlee Speice\Anaconda3\python.exe)</last-edited> | ||||||
|  |           <splitter-proportions> | ||||||
|  |             <option name="proportions"> | ||||||
|  |               <list> | ||||||
|  |                 <option value="0.2" /> | ||||||
|  |               </list> | ||||||
|  |             </option> | ||||||
|  |           </splitter-proportions> | ||||||
|  |         </settings> | ||||||
|  |       </state> | ||||||
|  |     </states> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="ProjectModuleManager"> | ||||||
|  |     <modules> | ||||||
|  |       <module fileurl="file://$PROJECT_DIR$/repod.iml" filepath="$PROJECT_DIR$/repod.iml" /> | ||||||
|  |     </modules> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										4
									
								
								Requirements
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								Requirements
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | pyramid>=1.6.1 | ||||||
|  | feedgen>=0.3.2 | ||||||
|  | PyYAML>=3.11 | ||||||
|  | requests>=2.9.1 | ||||||
							
								
								
									
										11
									
								
								example_conf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								example_conf.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | # Format: (all args are passed to __init__ as kwargs | ||||||
|  | # | ||||||
|  | # <mountpoint>: | ||||||
|  | #   class: <feed_class> | ||||||
|  | #   args: | ||||||
|  | #     key: value | ||||||
|  | subfactory-show: | ||||||
|  |   package: bassdrive | ||||||
|  |   class: BassdriveFeed | ||||||
|  |   args: | ||||||
|  |     url: http://archives.bassdrivearchive.com/1%20-%20Monday/Subfactory%20Show%20-%20DJ%20Spim/ | ||||||
							
								
								
									
										9
									
								
								repod.iml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								repod.iml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <module type="PYTHON_MODULE" version="4"> | ||||||
|  |   <component name="NewModuleRootManager" inherit-compiler-output="true"> | ||||||
|  |     <exclude-output /> | ||||||
|  |     <content url="file://$MODULE_DIR$" /> | ||||||
|  |     <orderEntry type="inheritedJdk" /> | ||||||
|  |     <orderEntry type="sourceFolder" forTests="false" /> | ||||||
|  |   </component> | ||||||
|  | </module> | ||||||
							
								
								
									
										0
									
								
								repod/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								repod/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										29
									
								
								repod/conf_parser.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								repod/conf_parser.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | """ | ||||||
|  | Given a configuration file, set up everything needed to kick | ||||||
|  | off the server. | ||||||
|  | """ | ||||||
|  | from importlib import import_module | ||||||
|  | import yaml | ||||||
|  | from pyramid.config import Configurator | ||||||
|  |  | ||||||
|  | # Needed for import_module call | ||||||
|  | # noinspection PyUnresolvedReferences | ||||||
|  | import modules | ||||||
|  |  | ||||||
|  | # Hardcoded, need to look up from users $HOME later | ||||||
|  | file_loc = 'example_conf.yaml' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def build_configuration(conf=file_loc) -> Configurator: | ||||||
|  |     with open(conf) as conf_file: | ||||||
|  |         conf_dict = yaml.load(conf_file) | ||||||
|  |         server_conf = Configurator() | ||||||
|  |         for mountpoint, feed in conf_dict.items(): | ||||||
|  |             feed_package = import_module('modules.' + feed['package']) | ||||||
|  |             feed_class = getattr(feed_package, feed['class']) | ||||||
|  |             feed_instance = feed_class(**feed['args']) | ||||||
|  |  | ||||||
|  |             server_conf.add_route(mountpoint, '/' + mountpoint + '/') | ||||||
|  |             server_conf.add_view(feed_instance.view, route_name=mountpoint) | ||||||
|  |  | ||||||
|  |         return server_conf | ||||||
							
								
								
									
										11
									
								
								repod/example_conf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								repod/example_conf.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | # Format: (all args are passed to __init__ as kwargs | ||||||
|  | # | ||||||
|  | # <mountpoint>: | ||||||
|  | #   class: <feed_class> | ||||||
|  | #   args: | ||||||
|  | #     key: value | ||||||
|  | subfactory-show: | ||||||
|  |   package: bassdrive | ||||||
|  |   class: BassdriveFeed | ||||||
|  |   args: | ||||||
|  |     url: http://archives.bassdrivearchive.com/1%20-%20Monday/Subfactory%20Show%20-%20DJ%20Spim/ | ||||||
							
								
								
									
										0
									
								
								repod/modules/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								repod/modules/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										74
									
								
								repod/modules/bassdrive.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								repod/modules/bassdrive.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | |||||||
|  | """ | ||||||
|  | Podcast provider for the Bassdrive Archives | ||||||
|  | """ | ||||||
|  | from html.parser import HTMLParser | ||||||
|  | from urllib.parse import unquote | ||||||
|  |  | ||||||
|  | import requests | ||||||
|  | from feedgen.feed import FeedGenerator | ||||||
|  |  | ||||||
|  | from podcast import BasePodcast | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class BassdriveParser(HTMLParser): | ||||||
|  |     links = [] | ||||||
|  |     record_link_text = False | ||||||
|  |     link_url = '' | ||||||
|  |  | ||||||
|  |     def handle_starttag(self, tag, attrs): | ||||||
|  |         href = '' | ||||||
|  |         for attr, val in attrs: | ||||||
|  |             if attr == 'href': | ||||||
|  |                 href = val | ||||||
|  |  | ||||||
|  |         if tag == 'a' and href.find('mp3') != -1: | ||||||
|  |             self.record_link_text = True | ||||||
|  |             self.link_url = href | ||||||
|  |  | ||||||
|  |     def handle_data(self, data): | ||||||
|  |         if self.record_link_text: | ||||||
|  |             print(self.link_url) | ||||||
|  |             self.links.append((data, self.link_url)) | ||||||
|  |             self.record_link_text = False | ||||||
|  |  | ||||||
|  |     def get_links(self): | ||||||
|  |         # Reverse to sort in descending date order | ||||||
|  |         self.links.reverse() | ||||||
|  |         return self.links | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class BassdriveFeed(BasePodcast): | ||||||
|  |     def __init__(self, *args, **kwargs): | ||||||
|  |         print(kwargs) | ||||||
|  |         self.url = kwargs['url'] | ||||||
|  |         # Get the title and DJ while handling trailing slash | ||||||
|  |         url_pretty = unquote(self.url) | ||||||
|  |         elems = filter(lambda x: x, url_pretty.split('/')) | ||||||
|  |         self.title, self.dj = list(elems)[-1].split(' - ') | ||||||
|  |  | ||||||
|  |     def build_feed(self): | ||||||
|  |         "Build the feed given our existing URL" | ||||||
|  |         # Get all the episodes | ||||||
|  |         page_content = str(requests.get(self.url).content) | ||||||
|  |         parser = BassdriveParser() | ||||||
|  |         parser.feed(page_content) | ||||||
|  |         links = parser.get_links() | ||||||
|  |  | ||||||
|  |         # And turn them into something usable | ||||||
|  |         fg = FeedGenerator() | ||||||
|  |         fg.load_extension('podcast') | ||||||
|  |         fg.id(self.url) | ||||||
|  |         fg.title(self.title) | ||||||
|  |         fg.description(self.title) | ||||||
|  |         fg.author({'name': self.dj}) | ||||||
|  |         fg.language('en') | ||||||
|  |         fg.link({'href': self.url, 'rel': 'alternate'}) | ||||||
|  |  | ||||||
|  |         for link in links: | ||||||
|  |             fe = fg.add_entry() | ||||||
|  |             fe.author({'name': self.dj}) | ||||||
|  |             fe.title(link[0]) | ||||||
|  |             fe.description(link[0]) | ||||||
|  |             fe.enclosure(self.url + link[1], 0, 'audio/mpeg') | ||||||
|  |  | ||||||
|  |         return fg | ||||||
							
								
								
									
										16
									
								
								repod/podcast.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								repod/podcast.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | """ | ||||||
|  | Base skeleton for what needs to be implemented by a podcast provider | ||||||
|  | """ | ||||||
|  | from feedgen.feed import FeedGenerator | ||||||
|  | from pyramid.response import Response | ||||||
|  |  | ||||||
|  | class BasePodcast(): | ||||||
|  |     def build_feed(self) -> FeedGenerator: | ||||||
|  |         "Return a list of all episodes, in descending date order" | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  |     def view(self, request): | ||||||
|  |         fg = self.build_feed() | ||||||
|  |         response = Response(fg.rss_str(pretty=True)) | ||||||
|  |         response.content_type = 'application/rss+xml' | ||||||
|  |         return response | ||||||
							
								
								
									
										10
									
								
								repod/server.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								repod/server.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | from wsgiref.simple_server import make_server | ||||||
|  | from conf_parser import build_configuration | ||||||
|  |  | ||||||
|  | def start_server(): | ||||||
|  |     app = build_configuration().make_wsgi_app() | ||||||
|  |     server = make_server('0.0.0.0', 8080, app) | ||||||
|  |     server.serve_forever() | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     start_server() | ||||||
		Reference in New Issue
	
	Block a user
	 Bradlee Speice
					Bradlee Speice