{"id":133,"date":"2014-07-31T19:48:23","date_gmt":"2014-07-31T19:48:23","guid":{"rendered":"http:\/\/www.piglets.org\/?p=133"},"modified":"2015-10-06T13:23:16","modified_gmt":"2015-10-06T13:23:16","slug":"python-script-to-randomise-an-m3u-playlist","status":"publish","type":"post","link":"https:\/\/www.piglets.org\/blog\/2014\/07\/31\/python-script-to-randomise-an-m3u-playlist\/","title":{"rendered":"Python script to randomise an m3u playlist"},"content":{"rendered":"<p>While I'm blogging scripts for <a title=\"Python script to add a file to a playlist\" href=\"http:\/\/www.piglets.org\/wp\/2014\/07\/31\/python-script-to-add-a-file-to-a-playlist\/\">playlist manipulation<\/a> here is one I use in a nightly cron job to shuffle our playlists so that various devices playing from them have some daily variety. All disclaimers apply, it's rough and ready but WorksForMe (TM).<\/p>\n<p>I have an entry in my crontab like this<\/p>\n<pre lang=\"crontab\">0 4 * * * \/home\/colin\/bin\/playlist-shuffle.py -q -i \/var\/media\/mp3\/A_Colin.m3u -o \/var\/media\/mp3\/A_Colin_Shuffle.m3u\r\n<\/pre>\n<p>which takes a static playlist and produces a nightly shuffled version.<\/p>\n<pre class=\"lang:python decode:true \" >\r\n#!\/usr\/bin\/env python\r\n#\r\n# Simple script to randomise an m3u playlist\r\n# Colin Turner &lt;ct@piglets.com&gt;\r\n# 2013\r\n# GPL v2\r\n#\r\n\r\nimport random\r\nimport re\r\n\r\n# We want to be able to process some command line options.\r\nfrom optparse import OptionParser\r\n\r\ndef process_lines(options, all_lines):\r\n  'process the list of all playlist lines into three chunks'\r\n  # Eventually we want to support several formats\r\n  m3u = True\r\n  extm3u = False\r\n  if options.verbose:\r\n    print \"Read %u lines...\" % len(all_lines)\r\n  header = list()\r\n  middle = list()\r\n  footer = list()\r\n  \r\n  # Check first line for #EXTM3U\r\n  if re.match(\"^#EXTM3U<img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.piglets.org\/blog\/wp-content\/ql-cache\/quicklatex.com-bd3b6312ed1ea1ae9a243bb21fc63d6e_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#34;&#44;&#32;&#97;&#108;&#108;&#95;&#108;&#105;&#110;&#101;&#115;&#091;&#48;&#093;&#41;&#58; &#32;&#32;&#32;&#32;&#105;&#102;&#32;&#111;&#112;&#116;&#105;&#111;&#110;&#115;&#46;&#118;&#101;&#114;&#98;&#111;&#115;&#101;&#58; &#32;&#32;&#32;&#32;&#32;&#32;&#112;&#114;&#105;&#110;&#116;&#32;&#34;&#69;&#88;&#84;&#77;&#51;&#85;&#32;&#102;&#111;&#114;&#109;&#97;&#116;&#32;&#102;&#105;&#108;&#101;&#46;&#46;&#46;&#34; &#32;&#32;&#32;&#32;&#101;&#120;&#116;&#109;&#51;&#117;&#32;&#61;&#32;&#84;&#114;&#117;&#101; &#32;&#32;&#32;&#32;&#104;&#101;&#97;&#100;&#101;&#114;&#46;&#97;&#112;&#112;&#101;&#110;&#100;&#40;&#97;&#108;&#108;&#95;&#108;&#105;&#110;&#101;&#115;&#091;&#48;&#093;&#41; &#32;&#32;&#32;&#32;&#100;&#101;&#108;&#32;&#97;&#108;&#108;&#95;&#108;&#105;&#110;&#101;&#115;&#091;&#48;&#093; &#32;&#32; &#32;&#32;&#108;&#111;&#111;&#112;&#32;&#61;&#32;&#48; &#32;&#32;&#119;&#104;&#105;&#108;&#101;&#32;&#108;&#111;&#111;&#112;&#32;&#60;&#32;&#108;&#101;&#110;&#40;&#97;&#108;&#108;&#95;&#108;&#105;&#110;&#101;&#115;&#41;&#58; &#32;&#32;&#32;&#32;&#35;&#32;&#69;&#97;&#99;&#104;&#32;&#39;&#105;&#116;&#101;&#109;&#39;&#32;&#109;&#97;&#121;&#32;&#98;&#101;&#32;&#109;&#117;&#108;&#116;&#105;&#108;&#105;&#110;&#101; &#32;&#32;&#32;&#32;&#105;&#116;&#101;&#109;&#32;&#61;&#32;&#108;&#105;&#115;&#116;&#40;&#41; &#32;&#32;&#32;&#32;&#105;&#102;&#32;&#114;&#101;&#46;&#109;&#97;&#116;&#99;&#104;&#40;&#34;&#94;&#35;&#69;&#88;&#84;&#73;&#78;&#70;&#46;&#42;\" title=\"Rendered by QuickLaTeX.com\" height=\"84\" width=\"593\" style=\"vertical-align: -4px;\"\/>\", all_lines[loop]):\r\n      item.append(all_lines[loop])\r\n      loop = loop + 1\r\n    # A proper regexp for filenames would be good\r\n    if loop &lt; len(all_lines):\r\n      item.append(all_lines[loop])\r\n      loop = loop + 1\r\n    if options.verbose: print item\r\n    middle.append(item)\r\n            \r\n  return (header, middle, footer)\r\n\r\ndef load_playlist(options):\r\n  'loads the playlist into an array of arrays'\r\n  if options.verbose:\r\n    print \"Reading playlist %s ...\" % options.in_filename\r\n  with open(options.in_filename, 'r') as file:\r\n    all_lines = file.readlines()\r\n  (header, middle, footer) = process_lines(options, all_lines)\r\n  return (header, middle, footer)\r\n\r\ndef write_playlist(options, header, middle, footer):\r\n  'writes the shuffled playlist'\r\n  if options.verbose:\r\n    print \"Writing playlist %s ...\" % options.out_filename\r\n  with open(options.out_filename, 'w') as file:\r\n    for line in header:\r\n      file.write(line)\r\n    for item in middle:\r\n      for line in item:\r\n        file.write(line)\r\n    for line in footer:\r\n      file.write(line)\r\n\r\n\r\ndef shuffle(options):\r\n  'perform the shuffle on the playlist'\r\n  # read the existing data into three arrays in a tuple\r\n  (header, middle, footer) = load_playlist(options)\r\n  # and shuffle the lines array\r\n  if options.verbose:\r\n    print \"Shuffling...\"\r\n  random.shuffle(middle)\r\n  # now spit them back out\r\n  write_playlist(options, header, middle, footer)\r\n\r\ndef print_banner():\r\n  print \"playlist-shuffle\"\r\n\r\ndef main():\r\n  'the main function that kicks everything else off'\r\n  \r\n  usage = \"usage: %prog [options] arg\"\r\n  parser = OptionParser(usage)\r\n  parser.add_option(\"-i\", \"--input-file\", dest=\"in_filename\",\r\n                    help=\"read playlist from FILENAME\")\r\n  parser.add_option(\"-o\", \"--output-file\", dest=\"out_filename\",\r\n                    help=\"write new playlist to FILENAME\")\r\n  parser.add_option(\"-v\", \"--verbose\",\r\n                    action=\"store_true\", dest=\"verbose\")\r\n  parser.add_option(\"-q\", \"--quiet\", default=False,\r\n                    action=\"store_true\", dest=\"quiet\")\r\n                    \r\n  (options, args) = parser.parse_args()\r\n#  if len(args) == 0:\r\n#      parser.error(\"use -h for more help\")\r\n  \r\n  if not options.quiet:\r\n    print_banner()\r\n  \r\n  shuffle(options)\r\n  \r\n  if not options.quiet:\r\n      print \"Playlist shuffle complete...\"\r\n  \r\n \r\nif  __name__ == '__main__':\r\n  main()\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>While I'm blogging scripts for playlist manipulation here is one I use in a nightly cron job to shuffle our playlists so that various devices playing from them have some daily variety. All disclaimers apply, it's rough and ready but WorksForMe (TM). I have an entry in my crontab like this 0 4 * * [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"vkexunit_cta_each_option":"","footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"enabled":false},"version":2}},"categories":[7,14],"tags":[55,56,57,118,58],"class_list":["post-133","post","type-post","status-publish","format-standard","hentry","category-11-free-software","category-17-python","tag-m3u","tag-mp3","tag-playlist","tag-7-programming","tag-python"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"jetpack_shortlink":"https:\/\/wp.me\/p52I4w-29","_links":{"self":[{"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/posts\/133","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/comments?post=133"}],"version-history":[{"count":5,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/posts\/133\/revisions"}],"predecessor-version":[{"id":345,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/posts\/133\/revisions\/345"}],"wp:attachment":[{"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/media?parent=133"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/categories?post=133"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/tags?post=133"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}