浏览代码

First pass at adding videogames

Colin Powell 2 年之前
父节点
当前提交
25c00d7f1b

+ 613 - 0
vrobbler/apps/profiles/migrations/0003_userprofile_twitch_client_id_and_more.py

@@ -0,0 +1,613 @@
+# Generated by Django 4.1.5 on 2023-03-05 00:05
+
+from django.db import migrations, models
+import encrypted_field.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("profiles", "0002_userprofile_lastfm_password_and_more"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="userprofile",
+            name="twitch_client_id",
+            field=models.CharField(blank=True, max_length=255, null=True),
+        ),
+        migrations.AddField(
+            model_name="userprofile",
+            name="twitch_client_secret",
+            field=encrypted_field.fields.EncryptedField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name="userprofile",
+            name="timezone",
+            field=models.CharField(
+                blank=True,
+                choices=[
+                    ("Pacific/Midway", "(GMT-1100) Pacific/Midway"),
+                    ("Pacific/Niue", "(GMT-1100) Pacific/Niue"),
+                    ("Pacific/Pago_Pago", "(GMT-1100) Pacific/Pago_Pago"),
+                    ("America/Adak", "(GMT-1000) America/Adak"),
+                    ("Pacific/Honolulu", "(GMT-1000) Pacific/Honolulu"),
+                    ("Pacific/Rarotonga", "(GMT-1000) Pacific/Rarotonga"),
+                    ("Pacific/Tahiti", "(GMT-1000) Pacific/Tahiti"),
+                    ("US/Hawaii", "(GMT-1000) US/Hawaii"),
+                    ("Pacific/Marquesas", "(GMT-0930) Pacific/Marquesas"),
+                    ("America/Anchorage", "(GMT-0900) America/Anchorage"),
+                    ("America/Juneau", "(GMT-0900) America/Juneau"),
+                    ("America/Metlakatla", "(GMT-0900) America/Metlakatla"),
+                    ("America/Nome", "(GMT-0900) America/Nome"),
+                    ("America/Sitka", "(GMT-0900) America/Sitka"),
+                    ("America/Yakutat", "(GMT-0900) America/Yakutat"),
+                    ("Pacific/Gambier", "(GMT-0900) Pacific/Gambier"),
+                    ("US/Alaska", "(GMT-0900) US/Alaska"),
+                    ("America/Los_Angeles", "(GMT-0800) America/Los_Angeles"),
+                    ("America/Tijuana", "(GMT-0800) America/Tijuana"),
+                    ("America/Vancouver", "(GMT-0800) America/Vancouver"),
+                    ("Canada/Pacific", "(GMT-0800) Canada/Pacific"),
+                    ("Pacific/Pitcairn", "(GMT-0800) Pacific/Pitcairn"),
+                    ("US/Pacific", "(GMT-0800) US/Pacific"),
+                    ("America/Boise", "(GMT-0700) America/Boise"),
+                    (
+                        "America/Cambridge_Bay",
+                        "(GMT-0700) America/Cambridge_Bay",
+                    ),
+                    (
+                        "America/Ciudad_Juarez",
+                        "(GMT-0700) America/Ciudad_Juarez",
+                    ),
+                    ("America/Creston", "(GMT-0700) America/Creston"),
+                    ("America/Dawson", "(GMT-0700) America/Dawson"),
+                    (
+                        "America/Dawson_Creek",
+                        "(GMT-0700) America/Dawson_Creek",
+                    ),
+                    ("America/Denver", "(GMT-0700) America/Denver"),
+                    ("America/Edmonton", "(GMT-0700) America/Edmonton"),
+                    ("America/Fort_Nelson", "(GMT-0700) America/Fort_Nelson"),
+                    ("America/Hermosillo", "(GMT-0700) America/Hermosillo"),
+                    ("America/Inuvik", "(GMT-0700) America/Inuvik"),
+                    ("America/Mazatlan", "(GMT-0700) America/Mazatlan"),
+                    ("America/Phoenix", "(GMT-0700) America/Phoenix"),
+                    ("America/Whitehorse", "(GMT-0700) America/Whitehorse"),
+                    ("America/Yellowknife", "(GMT-0700) America/Yellowknife"),
+                    ("Canada/Mountain", "(GMT-0700) Canada/Mountain"),
+                    ("US/Arizona", "(GMT-0700) US/Arizona"),
+                    ("US/Mountain", "(GMT-0700) US/Mountain"),
+                    (
+                        "America/Bahia_Banderas",
+                        "(GMT-0600) America/Bahia_Banderas",
+                    ),
+                    ("America/Belize", "(GMT-0600) America/Belize"),
+                    ("America/Chicago", "(GMT-0600) America/Chicago"),
+                    ("America/Chihuahua", "(GMT-0600) America/Chihuahua"),
+                    ("America/Costa_Rica", "(GMT-0600) America/Costa_Rica"),
+                    ("America/El_Salvador", "(GMT-0600) America/El_Salvador"),
+                    ("America/Guatemala", "(GMT-0600) America/Guatemala"),
+                    (
+                        "America/Indiana/Knox",
+                        "(GMT-0600) America/Indiana/Knox",
+                    ),
+                    (
+                        "America/Indiana/Tell_City",
+                        "(GMT-0600) America/Indiana/Tell_City",
+                    ),
+                    ("America/Managua", "(GMT-0600) America/Managua"),
+                    ("America/Matamoros", "(GMT-0600) America/Matamoros"),
+                    ("America/Menominee", "(GMT-0600) America/Menominee"),
+                    ("America/Merida", "(GMT-0600) America/Merida"),
+                    ("America/Mexico_City", "(GMT-0600) America/Mexico_City"),
+                    ("America/Monterrey", "(GMT-0600) America/Monterrey"),
+                    (
+                        "America/North_Dakota/Beulah",
+                        "(GMT-0600) America/North_Dakota/Beulah",
+                    ),
+                    (
+                        "America/North_Dakota/Center",
+                        "(GMT-0600) America/North_Dakota/Center",
+                    ),
+                    (
+                        "America/North_Dakota/New_Salem",
+                        "(GMT-0600) America/North_Dakota/New_Salem",
+                    ),
+                    ("America/Ojinaga", "(GMT-0600) America/Ojinaga"),
+                    (
+                        "America/Rankin_Inlet",
+                        "(GMT-0600) America/Rankin_Inlet",
+                    ),
+                    ("America/Regina", "(GMT-0600) America/Regina"),
+                    ("America/Resolute", "(GMT-0600) America/Resolute"),
+                    (
+                        "America/Swift_Current",
+                        "(GMT-0600) America/Swift_Current",
+                    ),
+                    ("America/Tegucigalpa", "(GMT-0600) America/Tegucigalpa"),
+                    ("America/Winnipeg", "(GMT-0600) America/Winnipeg"),
+                    ("Canada/Central", "(GMT-0600) Canada/Central"),
+                    ("Pacific/Galapagos", "(GMT-0600) Pacific/Galapagos"),
+                    ("US/Central", "(GMT-0600) US/Central"),
+                    ("America/Atikokan", "(GMT-0500) America/Atikokan"),
+                    ("America/Bogota", "(GMT-0500) America/Bogota"),
+                    ("America/Cancun", "(GMT-0500) America/Cancun"),
+                    ("America/Cayman", "(GMT-0500) America/Cayman"),
+                    ("America/Detroit", "(GMT-0500) America/Detroit"),
+                    ("America/Eirunepe", "(GMT-0500) America/Eirunepe"),
+                    ("America/Grand_Turk", "(GMT-0500) America/Grand_Turk"),
+                    ("America/Guayaquil", "(GMT-0500) America/Guayaquil"),
+                    ("America/Havana", "(GMT-0500) America/Havana"),
+                    (
+                        "America/Indiana/Indianapolis",
+                        "(GMT-0500) America/Indiana/Indianapolis",
+                    ),
+                    (
+                        "America/Indiana/Marengo",
+                        "(GMT-0500) America/Indiana/Marengo",
+                    ),
+                    (
+                        "America/Indiana/Petersburg",
+                        "(GMT-0500) America/Indiana/Petersburg",
+                    ),
+                    (
+                        "America/Indiana/Vevay",
+                        "(GMT-0500) America/Indiana/Vevay",
+                    ),
+                    (
+                        "America/Indiana/Vincennes",
+                        "(GMT-0500) America/Indiana/Vincennes",
+                    ),
+                    (
+                        "America/Indiana/Winamac",
+                        "(GMT-0500) America/Indiana/Winamac",
+                    ),
+                    ("America/Iqaluit", "(GMT-0500) America/Iqaluit"),
+                    ("America/Jamaica", "(GMT-0500) America/Jamaica"),
+                    (
+                        "America/Kentucky/Louisville",
+                        "(GMT-0500) America/Kentucky/Louisville",
+                    ),
+                    (
+                        "America/Kentucky/Monticello",
+                        "(GMT-0500) America/Kentucky/Monticello",
+                    ),
+                    ("America/Lima", "(GMT-0500) America/Lima"),
+                    ("America/Nassau", "(GMT-0500) America/Nassau"),
+                    ("America/New_York", "(GMT-0500) America/New_York"),
+                    ("America/Panama", "(GMT-0500) America/Panama"),
+                    (
+                        "America/Port-au-Prince",
+                        "(GMT-0500) America/Port-au-Prince",
+                    ),
+                    ("America/Rio_Branco", "(GMT-0500) America/Rio_Branco"),
+                    ("America/Toronto", "(GMT-0500) America/Toronto"),
+                    ("Canada/Eastern", "(GMT-0500) Canada/Eastern"),
+                    ("Pacific/Easter", "(GMT-0500) Pacific/Easter"),
+                    ("US/Eastern", "(GMT-0500) US/Eastern"),
+                    ("America/Anguilla", "(GMT-0400) America/Anguilla"),
+                    ("America/Antigua", "(GMT-0400) America/Antigua"),
+                    ("America/Aruba", "(GMT-0400) America/Aruba"),
+                    ("America/Barbados", "(GMT-0400) America/Barbados"),
+                    (
+                        "America/Blanc-Sablon",
+                        "(GMT-0400) America/Blanc-Sablon",
+                    ),
+                    ("America/Boa_Vista", "(GMT-0400) America/Boa_Vista"),
+                    (
+                        "America/Campo_Grande",
+                        "(GMT-0400) America/Campo_Grande",
+                    ),
+                    ("America/Caracas", "(GMT-0400) America/Caracas"),
+                    ("America/Cuiaba", "(GMT-0400) America/Cuiaba"),
+                    ("America/Curacao", "(GMT-0400) America/Curacao"),
+                    ("America/Dominica", "(GMT-0400) America/Dominica"),
+                    ("America/Glace_Bay", "(GMT-0400) America/Glace_Bay"),
+                    ("America/Goose_Bay", "(GMT-0400) America/Goose_Bay"),
+                    ("America/Grenada", "(GMT-0400) America/Grenada"),
+                    ("America/Guadeloupe", "(GMT-0400) America/Guadeloupe"),
+                    ("America/Guyana", "(GMT-0400) America/Guyana"),
+                    ("America/Halifax", "(GMT-0400) America/Halifax"),
+                    ("America/Kralendijk", "(GMT-0400) America/Kralendijk"),
+                    ("America/La_Paz", "(GMT-0400) America/La_Paz"),
+                    (
+                        "America/Lower_Princes",
+                        "(GMT-0400) America/Lower_Princes",
+                    ),
+                    ("America/Manaus", "(GMT-0400) America/Manaus"),
+                    ("America/Marigot", "(GMT-0400) America/Marigot"),
+                    ("America/Martinique", "(GMT-0400) America/Martinique"),
+                    ("America/Moncton", "(GMT-0400) America/Moncton"),
+                    ("America/Montserrat", "(GMT-0400) America/Montserrat"),
+                    (
+                        "America/Port_of_Spain",
+                        "(GMT-0400) America/Port_of_Spain",
+                    ),
+                    ("America/Porto_Velho", "(GMT-0400) America/Porto_Velho"),
+                    ("America/Puerto_Rico", "(GMT-0400) America/Puerto_Rico"),
+                    (
+                        "America/Santo_Domingo",
+                        "(GMT-0400) America/Santo_Domingo",
+                    ),
+                    (
+                        "America/St_Barthelemy",
+                        "(GMT-0400) America/St_Barthelemy",
+                    ),
+                    ("America/St_Kitts", "(GMT-0400) America/St_Kitts"),
+                    ("America/St_Lucia", "(GMT-0400) America/St_Lucia"),
+                    ("America/St_Thomas", "(GMT-0400) America/St_Thomas"),
+                    ("America/St_Vincent", "(GMT-0400) America/St_Vincent"),
+                    ("America/Thule", "(GMT-0400) America/Thule"),
+                    ("America/Tortola", "(GMT-0400) America/Tortola"),
+                    ("Atlantic/Bermuda", "(GMT-0400) Atlantic/Bermuda"),
+                    ("Canada/Atlantic", "(GMT-0400) Canada/Atlantic"),
+                    ("America/St_Johns", "(GMT-0330) America/St_Johns"),
+                    ("Canada/Newfoundland", "(GMT-0330) Canada/Newfoundland"),
+                    ("America/Araguaina", "(GMT-0300) America/Araguaina"),
+                    (
+                        "America/Argentina/Buenos_Aires",
+                        "(GMT-0300) America/Argentina/Buenos_Aires",
+                    ),
+                    (
+                        "America/Argentina/Catamarca",
+                        "(GMT-0300) America/Argentina/Catamarca",
+                    ),
+                    (
+                        "America/Argentina/Cordoba",
+                        "(GMT-0300) America/Argentina/Cordoba",
+                    ),
+                    (
+                        "America/Argentina/Jujuy",
+                        "(GMT-0300) America/Argentina/Jujuy",
+                    ),
+                    (
+                        "America/Argentina/La_Rioja",
+                        "(GMT-0300) America/Argentina/La_Rioja",
+                    ),
+                    (
+                        "America/Argentina/Mendoza",
+                        "(GMT-0300) America/Argentina/Mendoza",
+                    ),
+                    (
+                        "America/Argentina/Rio_Gallegos",
+                        "(GMT-0300) America/Argentina/Rio_Gallegos",
+                    ),
+                    (
+                        "America/Argentina/Salta",
+                        "(GMT-0300) America/Argentina/Salta",
+                    ),
+                    (
+                        "America/Argentina/San_Juan",
+                        "(GMT-0300) America/Argentina/San_Juan",
+                    ),
+                    (
+                        "America/Argentina/San_Luis",
+                        "(GMT-0300) America/Argentina/San_Luis",
+                    ),
+                    (
+                        "America/Argentina/Tucuman",
+                        "(GMT-0300) America/Argentina/Tucuman",
+                    ),
+                    (
+                        "America/Argentina/Ushuaia",
+                        "(GMT-0300) America/Argentina/Ushuaia",
+                    ),
+                    ("America/Asuncion", "(GMT-0300) America/Asuncion"),
+                    ("America/Bahia", "(GMT-0300) America/Bahia"),
+                    ("America/Belem", "(GMT-0300) America/Belem"),
+                    ("America/Cayenne", "(GMT-0300) America/Cayenne"),
+                    ("America/Fortaleza", "(GMT-0300) America/Fortaleza"),
+                    ("America/Maceio", "(GMT-0300) America/Maceio"),
+                    ("America/Miquelon", "(GMT-0300) America/Miquelon"),
+                    ("America/Montevideo", "(GMT-0300) America/Montevideo"),
+                    ("America/Nuuk", "(GMT-0300) America/Nuuk"),
+                    ("America/Paramaribo", "(GMT-0300) America/Paramaribo"),
+                    (
+                        "America/Punta_Arenas",
+                        "(GMT-0300) America/Punta_Arenas",
+                    ),
+                    ("America/Recife", "(GMT-0300) America/Recife"),
+                    ("America/Santarem", "(GMT-0300) America/Santarem"),
+                    ("America/Santiago", "(GMT-0300) America/Santiago"),
+                    ("America/Sao_Paulo", "(GMT-0300) America/Sao_Paulo"),
+                    ("Antarctica/Palmer", "(GMT-0300) Antarctica/Palmer"),
+                    ("Antarctica/Rothera", "(GMT-0300) Antarctica/Rothera"),
+                    ("Atlantic/Stanley", "(GMT-0300) Atlantic/Stanley"),
+                    ("America/Noronha", "(GMT-0200) America/Noronha"),
+                    (
+                        "Atlantic/South_Georgia",
+                        "(GMT-0200) Atlantic/South_Georgia",
+                    ),
+                    (
+                        "America/Scoresbysund",
+                        "(GMT-0100) America/Scoresbysund",
+                    ),
+                    ("Atlantic/Azores", "(GMT-0100) Atlantic/Azores"),
+                    ("Atlantic/Cape_Verde", "(GMT-0100) Atlantic/Cape_Verde"),
+                    ("Africa/Abidjan", "(GMT+0000) Africa/Abidjan"),
+                    ("Africa/Accra", "(GMT+0000) Africa/Accra"),
+                    ("Africa/Bamako", "(GMT+0000) Africa/Bamako"),
+                    ("Africa/Banjul", "(GMT+0000) Africa/Banjul"),
+                    ("Africa/Bissau", "(GMT+0000) Africa/Bissau"),
+                    ("Africa/Conakry", "(GMT+0000) Africa/Conakry"),
+                    ("Africa/Dakar", "(GMT+0000) Africa/Dakar"),
+                    ("Africa/Freetown", "(GMT+0000) Africa/Freetown"),
+                    ("Africa/Lome", "(GMT+0000) Africa/Lome"),
+                    ("Africa/Monrovia", "(GMT+0000) Africa/Monrovia"),
+                    ("Africa/Nouakchott", "(GMT+0000) Africa/Nouakchott"),
+                    ("Africa/Ouagadougou", "(GMT+0000) Africa/Ouagadougou"),
+                    ("Africa/Sao_Tome", "(GMT+0000) Africa/Sao_Tome"),
+                    (
+                        "America/Danmarkshavn",
+                        "(GMT+0000) America/Danmarkshavn",
+                    ),
+                    ("Antarctica/Troll", "(GMT+0000) Antarctica/Troll"),
+                    ("Atlantic/Canary", "(GMT+0000) Atlantic/Canary"),
+                    ("Atlantic/Faroe", "(GMT+0000) Atlantic/Faroe"),
+                    ("Atlantic/Madeira", "(GMT+0000) Atlantic/Madeira"),
+                    ("Atlantic/Reykjavik", "(GMT+0000) Atlantic/Reykjavik"),
+                    ("Atlantic/St_Helena", "(GMT+0000) Atlantic/St_Helena"),
+                    ("Europe/Dublin", "(GMT+0000) Europe/Dublin"),
+                    ("Europe/Guernsey", "(GMT+0000) Europe/Guernsey"),
+                    ("Europe/Isle_of_Man", "(GMT+0000) Europe/Isle_of_Man"),
+                    ("Europe/Jersey", "(GMT+0000) Europe/Jersey"),
+                    ("Europe/Lisbon", "(GMT+0000) Europe/Lisbon"),
+                    ("Europe/London", "(GMT+0000) Europe/London"),
+                    ("GMT", "(GMT+0000) GMT"),
+                    ("UTC", "(GMT+0000) UTC"),
+                    ("Africa/Algiers", "(GMT+0100) Africa/Algiers"),
+                    ("Africa/Bangui", "(GMT+0100) Africa/Bangui"),
+                    ("Africa/Brazzaville", "(GMT+0100) Africa/Brazzaville"),
+                    ("Africa/Casablanca", "(GMT+0100) Africa/Casablanca"),
+                    ("Africa/Ceuta", "(GMT+0100) Africa/Ceuta"),
+                    ("Africa/Douala", "(GMT+0100) Africa/Douala"),
+                    ("Africa/El_Aaiun", "(GMT+0100) Africa/El_Aaiun"),
+                    ("Africa/Kinshasa", "(GMT+0100) Africa/Kinshasa"),
+                    ("Africa/Lagos", "(GMT+0100) Africa/Lagos"),
+                    ("Africa/Libreville", "(GMT+0100) Africa/Libreville"),
+                    ("Africa/Luanda", "(GMT+0100) Africa/Luanda"),
+                    ("Africa/Malabo", "(GMT+0100) Africa/Malabo"),
+                    ("Africa/Ndjamena", "(GMT+0100) Africa/Ndjamena"),
+                    ("Africa/Niamey", "(GMT+0100) Africa/Niamey"),
+                    ("Africa/Porto-Novo", "(GMT+0100) Africa/Porto-Novo"),
+                    ("Africa/Tunis", "(GMT+0100) Africa/Tunis"),
+                    ("Arctic/Longyearbyen", "(GMT+0100) Arctic/Longyearbyen"),
+                    ("Europe/Amsterdam", "(GMT+0100) Europe/Amsterdam"),
+                    ("Europe/Andorra", "(GMT+0100) Europe/Andorra"),
+                    ("Europe/Belgrade", "(GMT+0100) Europe/Belgrade"),
+                    ("Europe/Berlin", "(GMT+0100) Europe/Berlin"),
+                    ("Europe/Bratislava", "(GMT+0100) Europe/Bratislava"),
+                    ("Europe/Brussels", "(GMT+0100) Europe/Brussels"),
+                    ("Europe/Budapest", "(GMT+0100) Europe/Budapest"),
+                    ("Europe/Busingen", "(GMT+0100) Europe/Busingen"),
+                    ("Europe/Copenhagen", "(GMT+0100) Europe/Copenhagen"),
+                    ("Europe/Gibraltar", "(GMT+0100) Europe/Gibraltar"),
+                    ("Europe/Ljubljana", "(GMT+0100) Europe/Ljubljana"),
+                    ("Europe/Luxembourg", "(GMT+0100) Europe/Luxembourg"),
+                    ("Europe/Madrid", "(GMT+0100) Europe/Madrid"),
+                    ("Europe/Malta", "(GMT+0100) Europe/Malta"),
+                    ("Europe/Monaco", "(GMT+0100) Europe/Monaco"),
+                    ("Europe/Oslo", "(GMT+0100) Europe/Oslo"),
+                    ("Europe/Paris", "(GMT+0100) Europe/Paris"),
+                    ("Europe/Podgorica", "(GMT+0100) Europe/Podgorica"),
+                    ("Europe/Prague", "(GMT+0100) Europe/Prague"),
+                    ("Europe/Rome", "(GMT+0100) Europe/Rome"),
+                    ("Europe/San_Marino", "(GMT+0100) Europe/San_Marino"),
+                    ("Europe/Sarajevo", "(GMT+0100) Europe/Sarajevo"),
+                    ("Europe/Skopje", "(GMT+0100) Europe/Skopje"),
+                    ("Europe/Stockholm", "(GMT+0100) Europe/Stockholm"),
+                    ("Europe/Tirane", "(GMT+0100) Europe/Tirane"),
+                    ("Europe/Vaduz", "(GMT+0100) Europe/Vaduz"),
+                    ("Europe/Vatican", "(GMT+0100) Europe/Vatican"),
+                    ("Europe/Vienna", "(GMT+0100) Europe/Vienna"),
+                    ("Europe/Warsaw", "(GMT+0100) Europe/Warsaw"),
+                    ("Europe/Zagreb", "(GMT+0100) Europe/Zagreb"),
+                    ("Europe/Zurich", "(GMT+0100) Europe/Zurich"),
+                    ("Africa/Blantyre", "(GMT+0200) Africa/Blantyre"),
+                    ("Africa/Bujumbura", "(GMT+0200) Africa/Bujumbura"),
+                    ("Africa/Cairo", "(GMT+0200) Africa/Cairo"),
+                    ("Africa/Gaborone", "(GMT+0200) Africa/Gaborone"),
+                    ("Africa/Harare", "(GMT+0200) Africa/Harare"),
+                    ("Africa/Johannesburg", "(GMT+0200) Africa/Johannesburg"),
+                    ("Africa/Juba", "(GMT+0200) Africa/Juba"),
+                    ("Africa/Khartoum", "(GMT+0200) Africa/Khartoum"),
+                    ("Africa/Kigali", "(GMT+0200) Africa/Kigali"),
+                    ("Africa/Lubumbashi", "(GMT+0200) Africa/Lubumbashi"),
+                    ("Africa/Lusaka", "(GMT+0200) Africa/Lusaka"),
+                    ("Africa/Maputo", "(GMT+0200) Africa/Maputo"),
+                    ("Africa/Maseru", "(GMT+0200) Africa/Maseru"),
+                    ("Africa/Mbabane", "(GMT+0200) Africa/Mbabane"),
+                    ("Africa/Tripoli", "(GMT+0200) Africa/Tripoli"),
+                    ("Africa/Windhoek", "(GMT+0200) Africa/Windhoek"),
+                    ("Asia/Beirut", "(GMT+0200) Asia/Beirut"),
+                    ("Asia/Famagusta", "(GMT+0200) Asia/Famagusta"),
+                    ("Asia/Gaza", "(GMT+0200) Asia/Gaza"),
+                    ("Asia/Hebron", "(GMT+0200) Asia/Hebron"),
+                    ("Asia/Jerusalem", "(GMT+0200) Asia/Jerusalem"),
+                    ("Asia/Nicosia", "(GMT+0200) Asia/Nicosia"),
+                    ("Europe/Athens", "(GMT+0200) Europe/Athens"),
+                    ("Europe/Bucharest", "(GMT+0200) Europe/Bucharest"),
+                    ("Europe/Chisinau", "(GMT+0200) Europe/Chisinau"),
+                    ("Europe/Helsinki", "(GMT+0200) Europe/Helsinki"),
+                    ("Europe/Kaliningrad", "(GMT+0200) Europe/Kaliningrad"),
+                    ("Europe/Kyiv", "(GMT+0200) Europe/Kyiv"),
+                    ("Europe/Mariehamn", "(GMT+0200) Europe/Mariehamn"),
+                    ("Europe/Riga", "(GMT+0200) Europe/Riga"),
+                    ("Europe/Sofia", "(GMT+0200) Europe/Sofia"),
+                    ("Europe/Tallinn", "(GMT+0200) Europe/Tallinn"),
+                    ("Europe/Vilnius", "(GMT+0200) Europe/Vilnius"),
+                    ("Africa/Addis_Ababa", "(GMT+0300) Africa/Addis_Ababa"),
+                    ("Africa/Asmara", "(GMT+0300) Africa/Asmara"),
+                    (
+                        "Africa/Dar_es_Salaam",
+                        "(GMT+0300) Africa/Dar_es_Salaam",
+                    ),
+                    ("Africa/Djibouti", "(GMT+0300) Africa/Djibouti"),
+                    ("Africa/Kampala", "(GMT+0300) Africa/Kampala"),
+                    ("Africa/Mogadishu", "(GMT+0300) Africa/Mogadishu"),
+                    ("Africa/Nairobi", "(GMT+0300) Africa/Nairobi"),
+                    ("Antarctica/Syowa", "(GMT+0300) Antarctica/Syowa"),
+                    ("Asia/Aden", "(GMT+0300) Asia/Aden"),
+                    ("Asia/Amman", "(GMT+0300) Asia/Amman"),
+                    ("Asia/Baghdad", "(GMT+0300) Asia/Baghdad"),
+                    ("Asia/Bahrain", "(GMT+0300) Asia/Bahrain"),
+                    ("Asia/Damascus", "(GMT+0300) Asia/Damascus"),
+                    ("Asia/Kuwait", "(GMT+0300) Asia/Kuwait"),
+                    ("Asia/Qatar", "(GMT+0300) Asia/Qatar"),
+                    ("Asia/Riyadh", "(GMT+0300) Asia/Riyadh"),
+                    ("Europe/Istanbul", "(GMT+0300) Europe/Istanbul"),
+                    ("Europe/Kirov", "(GMT+0300) Europe/Kirov"),
+                    ("Europe/Minsk", "(GMT+0300) Europe/Minsk"),
+                    ("Europe/Moscow", "(GMT+0300) Europe/Moscow"),
+                    ("Europe/Simferopol", "(GMT+0300) Europe/Simferopol"),
+                    ("Europe/Volgograd", "(GMT+0300) Europe/Volgograd"),
+                    ("Indian/Antananarivo", "(GMT+0300) Indian/Antananarivo"),
+                    ("Indian/Comoro", "(GMT+0300) Indian/Comoro"),
+                    ("Indian/Mayotte", "(GMT+0300) Indian/Mayotte"),
+                    ("Asia/Tehran", "(GMT+0330) Asia/Tehran"),
+                    ("Asia/Baku", "(GMT+0400) Asia/Baku"),
+                    ("Asia/Dubai", "(GMT+0400) Asia/Dubai"),
+                    ("Asia/Muscat", "(GMT+0400) Asia/Muscat"),
+                    ("Asia/Tbilisi", "(GMT+0400) Asia/Tbilisi"),
+                    ("Asia/Yerevan", "(GMT+0400) Asia/Yerevan"),
+                    ("Europe/Astrakhan", "(GMT+0400) Europe/Astrakhan"),
+                    ("Europe/Samara", "(GMT+0400) Europe/Samara"),
+                    ("Europe/Saratov", "(GMT+0400) Europe/Saratov"),
+                    ("Europe/Ulyanovsk", "(GMT+0400) Europe/Ulyanovsk"),
+                    ("Indian/Mahe", "(GMT+0400) Indian/Mahe"),
+                    ("Indian/Mauritius", "(GMT+0400) Indian/Mauritius"),
+                    ("Indian/Reunion", "(GMT+0400) Indian/Reunion"),
+                    ("Asia/Kabul", "(GMT+0430) Asia/Kabul"),
+                    ("Antarctica/Mawson", "(GMT+0500) Antarctica/Mawson"),
+                    ("Asia/Aqtau", "(GMT+0500) Asia/Aqtau"),
+                    ("Asia/Aqtobe", "(GMT+0500) Asia/Aqtobe"),
+                    ("Asia/Ashgabat", "(GMT+0500) Asia/Ashgabat"),
+                    ("Asia/Atyrau", "(GMT+0500) Asia/Atyrau"),
+                    ("Asia/Dushanbe", "(GMT+0500) Asia/Dushanbe"),
+                    ("Asia/Karachi", "(GMT+0500) Asia/Karachi"),
+                    ("Asia/Oral", "(GMT+0500) Asia/Oral"),
+                    ("Asia/Qyzylorda", "(GMT+0500) Asia/Qyzylorda"),
+                    ("Asia/Samarkand", "(GMT+0500) Asia/Samarkand"),
+                    ("Asia/Tashkent", "(GMT+0500) Asia/Tashkent"),
+                    ("Asia/Yekaterinburg", "(GMT+0500) Asia/Yekaterinburg"),
+                    ("Indian/Kerguelen", "(GMT+0500) Indian/Kerguelen"),
+                    ("Indian/Maldives", "(GMT+0500) Indian/Maldives"),
+                    ("Asia/Colombo", "(GMT+0530) Asia/Colombo"),
+                    ("Asia/Kolkata", "(GMT+0530) Asia/Kolkata"),
+                    ("Asia/Kathmandu", "(GMT+0545) Asia/Kathmandu"),
+                    ("Antarctica/Vostok", "(GMT+0600) Antarctica/Vostok"),
+                    ("Asia/Almaty", "(GMT+0600) Asia/Almaty"),
+                    ("Asia/Bishkek", "(GMT+0600) Asia/Bishkek"),
+                    ("Asia/Dhaka", "(GMT+0600) Asia/Dhaka"),
+                    ("Asia/Omsk", "(GMT+0600) Asia/Omsk"),
+                    ("Asia/Qostanay", "(GMT+0600) Asia/Qostanay"),
+                    ("Asia/Thimphu", "(GMT+0600) Asia/Thimphu"),
+                    ("Asia/Urumqi", "(GMT+0600) Asia/Urumqi"),
+                    ("Indian/Chagos", "(GMT+0600) Indian/Chagos"),
+                    ("Asia/Yangon", "(GMT+0630) Asia/Yangon"),
+                    ("Indian/Cocos", "(GMT+0630) Indian/Cocos"),
+                    ("Antarctica/Davis", "(GMT+0700) Antarctica/Davis"),
+                    ("Asia/Bangkok", "(GMT+0700) Asia/Bangkok"),
+                    ("Asia/Barnaul", "(GMT+0700) Asia/Barnaul"),
+                    ("Asia/Ho_Chi_Minh", "(GMT+0700) Asia/Ho_Chi_Minh"),
+                    ("Asia/Hovd", "(GMT+0700) Asia/Hovd"),
+                    ("Asia/Jakarta", "(GMT+0700) Asia/Jakarta"),
+                    ("Asia/Krasnoyarsk", "(GMT+0700) Asia/Krasnoyarsk"),
+                    ("Asia/Novokuznetsk", "(GMT+0700) Asia/Novokuznetsk"),
+                    ("Asia/Novosibirsk", "(GMT+0700) Asia/Novosibirsk"),
+                    ("Asia/Phnom_Penh", "(GMT+0700) Asia/Phnom_Penh"),
+                    ("Asia/Pontianak", "(GMT+0700) Asia/Pontianak"),
+                    ("Asia/Tomsk", "(GMT+0700) Asia/Tomsk"),
+                    ("Asia/Vientiane", "(GMT+0700) Asia/Vientiane"),
+                    ("Indian/Christmas", "(GMT+0700) Indian/Christmas"),
+                    ("Asia/Brunei", "(GMT+0800) Asia/Brunei"),
+                    ("Asia/Choibalsan", "(GMT+0800) Asia/Choibalsan"),
+                    ("Asia/Hong_Kong", "(GMT+0800) Asia/Hong_Kong"),
+                    ("Asia/Irkutsk", "(GMT+0800) Asia/Irkutsk"),
+                    ("Asia/Kuala_Lumpur", "(GMT+0800) Asia/Kuala_Lumpur"),
+                    ("Asia/Kuching", "(GMT+0800) Asia/Kuching"),
+                    ("Asia/Macau", "(GMT+0800) Asia/Macau"),
+                    ("Asia/Makassar", "(GMT+0800) Asia/Makassar"),
+                    ("Asia/Manila", "(GMT+0800) Asia/Manila"),
+                    ("Asia/Shanghai", "(GMT+0800) Asia/Shanghai"),
+                    ("Asia/Singapore", "(GMT+0800) Asia/Singapore"),
+                    ("Asia/Taipei", "(GMT+0800) Asia/Taipei"),
+                    ("Asia/Ulaanbaatar", "(GMT+0800) Asia/Ulaanbaatar"),
+                    ("Australia/Perth", "(GMT+0800) Australia/Perth"),
+                    ("Australia/Eucla", "(GMT+0845) Australia/Eucla"),
+                    ("Asia/Chita", "(GMT+0900) Asia/Chita"),
+                    ("Asia/Dili", "(GMT+0900) Asia/Dili"),
+                    ("Asia/Jayapura", "(GMT+0900) Asia/Jayapura"),
+                    ("Asia/Khandyga", "(GMT+0900) Asia/Khandyga"),
+                    ("Asia/Pyongyang", "(GMT+0900) Asia/Pyongyang"),
+                    ("Asia/Seoul", "(GMT+0900) Asia/Seoul"),
+                    ("Asia/Tokyo", "(GMT+0900) Asia/Tokyo"),
+                    ("Asia/Yakutsk", "(GMT+0900) Asia/Yakutsk"),
+                    ("Pacific/Palau", "(GMT+0900) Pacific/Palau"),
+                    ("Australia/Darwin", "(GMT+0930) Australia/Darwin"),
+                    (
+                        "Antarctica/DumontDUrville",
+                        "(GMT+1000) Antarctica/DumontDUrville",
+                    ),
+                    ("Asia/Ust-Nera", "(GMT+1000) Asia/Ust-Nera"),
+                    ("Asia/Vladivostok", "(GMT+1000) Asia/Vladivostok"),
+                    ("Australia/Brisbane", "(GMT+1000) Australia/Brisbane"),
+                    ("Australia/Lindeman", "(GMT+1000) Australia/Lindeman"),
+                    ("Pacific/Chuuk", "(GMT+1000) Pacific/Chuuk"),
+                    ("Pacific/Guam", "(GMT+1000) Pacific/Guam"),
+                    (
+                        "Pacific/Port_Moresby",
+                        "(GMT+1000) Pacific/Port_Moresby",
+                    ),
+                    ("Pacific/Saipan", "(GMT+1000) Pacific/Saipan"),
+                    ("Australia/Adelaide", "(GMT+1030) Australia/Adelaide"),
+                    (
+                        "Australia/Broken_Hill",
+                        "(GMT+1030) Australia/Broken_Hill",
+                    ),
+                    ("Antarctica/Casey", "(GMT+1100) Antarctica/Casey"),
+                    (
+                        "Antarctica/Macquarie",
+                        "(GMT+1100) Antarctica/Macquarie",
+                    ),
+                    ("Asia/Magadan", "(GMT+1100) Asia/Magadan"),
+                    ("Asia/Sakhalin", "(GMT+1100) Asia/Sakhalin"),
+                    ("Asia/Srednekolymsk", "(GMT+1100) Asia/Srednekolymsk"),
+                    ("Australia/Hobart", "(GMT+1100) Australia/Hobart"),
+                    ("Australia/Lord_Howe", "(GMT+1100) Australia/Lord_Howe"),
+                    ("Australia/Melbourne", "(GMT+1100) Australia/Melbourne"),
+                    ("Australia/Sydney", "(GMT+1100) Australia/Sydney"),
+                    (
+                        "Pacific/Bougainville",
+                        "(GMT+1100) Pacific/Bougainville",
+                    ),
+                    ("Pacific/Efate", "(GMT+1100) Pacific/Efate"),
+                    ("Pacific/Guadalcanal", "(GMT+1100) Pacific/Guadalcanal"),
+                    ("Pacific/Kosrae", "(GMT+1100) Pacific/Kosrae"),
+                    ("Pacific/Noumea", "(GMT+1100) Pacific/Noumea"),
+                    ("Pacific/Pohnpei", "(GMT+1100) Pacific/Pohnpei"),
+                    ("Asia/Anadyr", "(GMT+1200) Asia/Anadyr"),
+                    ("Asia/Kamchatka", "(GMT+1200) Asia/Kamchatka"),
+                    ("Pacific/Fiji", "(GMT+1200) Pacific/Fiji"),
+                    ("Pacific/Funafuti", "(GMT+1200) Pacific/Funafuti"),
+                    ("Pacific/Kwajalein", "(GMT+1200) Pacific/Kwajalein"),
+                    ("Pacific/Majuro", "(GMT+1200) Pacific/Majuro"),
+                    ("Pacific/Nauru", "(GMT+1200) Pacific/Nauru"),
+                    ("Pacific/Norfolk", "(GMT+1200) Pacific/Norfolk"),
+                    ("Pacific/Tarawa", "(GMT+1200) Pacific/Tarawa"),
+                    ("Pacific/Wake", "(GMT+1200) Pacific/Wake"),
+                    ("Pacific/Wallis", "(GMT+1200) Pacific/Wallis"),
+                    ("Antarctica/McMurdo", "(GMT+1300) Antarctica/McMurdo"),
+                    ("Pacific/Apia", "(GMT+1300) Pacific/Apia"),
+                    ("Pacific/Auckland", "(GMT+1300) Pacific/Auckland"),
+                    ("Pacific/Fakaofo", "(GMT+1300) Pacific/Fakaofo"),
+                    ("Pacific/Kanton", "(GMT+1300) Pacific/Kanton"),
+                    ("Pacific/Tongatapu", "(GMT+1300) Pacific/Tongatapu"),
+                    ("Pacific/Chatham", "(GMT+1345) Pacific/Chatham"),
+                    ("Pacific/Kiritimati", "(GMT+1400) Pacific/Kiritimati"),
+                ],
+                max_length=255,
+                null=True,
+            ),
+        ),
+    ]

+ 24 - 0
vrobbler/apps/profiles/migrations/0004_userprofile_twitch_token_and_more.py

@@ -0,0 +1,24 @@
+# Generated by Django 4.1.5 on 2023-03-05 01:04
+
+from django.db import migrations, models
+import encrypted_field.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("profiles", "0003_userprofile_twitch_client_id_and_more"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="userprofile",
+            name="twitch_token",
+            field=encrypted_field.fields.EncryptedField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name="userprofile",
+            name="twitch_token_expires",
+            field=models.DateTimeField(blank=True, null=True),
+        ),
+    ]

+ 23 - 0
vrobbler/apps/scrobbles/migrations/0025_scrobble_long_play_complete_and_more.py

@@ -0,0 +1,23 @@
+# Generated by Django 4.1.5 on 2023-03-04 23:54
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("scrobbles", "0024_chartrecord_period_end_chartrecord_period_start"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="scrobble",
+            name="long_play_complete",
+            field=models.BooleanField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name="scrobble",
+            name="videogame_minutes_played",
+            field=models.IntegerField(blank=True, null=True),
+        ),
+    ]

+ 25 - 0
vrobbler/apps/scrobbles/migrations/0026_scrobble_video_game.py

@@ -0,0 +1,25 @@
+# Generated by Django 4.1.5 on 2023-03-05 07:16
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("videogames", "0001_initial"),
+        ("scrobbles", "0025_scrobble_long_play_complete_and_more"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="scrobble",
+            name="video_game",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.DO_NOTHING,
+                to="videogames.videogame",
+            ),
+        ),
+    ]

+ 0 - 0
vrobbler/apps/videogames/__init__.py


+ 26 - 0
vrobbler/apps/videogames/admin.py

@@ -0,0 +1,26 @@
+from django.contrib import admin
+
+from videogames.models import VideoGame, VideoGameCollection
+
+from scrobbles.admin import ScrobbleInline
+
+
+@admin.register(VideoGameCollection)
+class VideoGameCollectionAdmin(admin.ModelAdmin):
+    date_hierarchy = "created"
+    list_display = (
+        "name",
+        "igdb_id",
+        "uuid",
+    )
+    ordering = ("-created",)
+
+
+@admin.register(VideoGame)
+class GameAdmin(admin.ModelAdmin):
+    date_hierarchy = "created"
+    list_display = ("title", "igdb_id")
+    ordering = ("-created",)
+    inlines = [
+        ScrobbleInline,
+    ]

+ 85 - 0
vrobbler/apps/videogames/igdb.py

@@ -0,0 +1,85 @@
+import json
+import logging
+from datetime import datetime
+from typing import Dict, Tuple
+
+import pytz
+import requests
+from django.contrib.auth import get_user_model
+
+TWITCH_AUTH_BASE = "https://id.twitch.tv/"
+REFRESH_TOKEN_URL = (
+    TWITCH_AUTH_BASE
+    + "oauth2/token?client_id={id}&client_secret={secret}&grant_type=client_credentials"
+)
+SEARCH_URL = "https://api.igdb.com/v4/search"
+GAMES_URL = "https://api.igdb.com/v4/games"
+ALT_NAMES_URL = "https://api.igdb.com/v4/alternative_names"
+SCREENSHOT_URL = "https://api.igdb.com/v4/screenshots"
+COVER_URL = "https://api.igdb.com/v4/covers"
+
+logger = logging.getLogger(__name__)
+
+User = get_user_model()
+
+
+def refresh_igdb_api_token(user_id: int) -> Tuple[str, int]:
+    user = User.objects.get(id=user_id)
+    token_url = REFRESH_TOKEN_URL.format(
+        id=user.profile.twitch_client_id,
+        secret=user.profile.twitch_client_secret,
+    )
+    response = requests.post(token_url)
+    results = json.loads(response.content)
+    return results.get("access_token"), results.get("expires_in")
+
+
+def lookup_game_from_igdb(client_id: str, token: str, game_id: str) -> Dict:
+    headers = {
+        "Authorization": f"Bearer {token}",
+        "Client-ID": client_id,
+    }
+    fields = "id,name,alternative_names.*,release_dates.*,cover.*,screenshots.*,rating,rating_count"
+
+    game_dict = {}
+    if game_id:
+        body = f"fields {fields}; where id = {game_id};"
+        response = requests.post(GAMES_URL, data=body, headers=headers)
+        results = json.loads(response.content)
+        if results:
+            game = results[0]
+            logger.debug(game)
+
+            alt_name = None
+            if "alternative_names" in game.keys():
+                alt_name = game.get("alternative_names")[0].get("name")
+            screenshot_url = None
+            if "screenshots" in game.keys():
+                screenshot_url = "https:" + game.get("screenshots")[0].get(
+                    "url"
+                ).replace("t_thumb", "t_screenshot_big_2x")
+            cover_url = None
+            if "cover" in game.keys():
+                cover_url = "https:" + game.get("cover").get("url").replace(
+                    "t_thumb", "t_cover_big_2x"
+                )
+            release_date = None
+            if "release_dates" in game.keys():
+                release_date = game.get("release_dates")[0].get("date")
+                if release_date:
+                    release_date = datetime.utcfromtimestamp(
+                        release_date
+                    ).replace(tzinfo=pytz.utc)
+
+            game_dict = {
+                "igdb_id": game.get("id"),
+                "title": game.get("name"),
+                "alternative_name": alt_name,
+                "screenshot_url": screenshot_url,
+                "cover_url": cover_url,
+                "rating": game.get("rating"),
+                "rating_count": game.get("rating_count"),
+                "release_date": release_date,
+            }
+
+    return game_dict

+ 125 - 0
vrobbler/apps/videogames/migrations/0001_initial.py

@@ -0,0 +1,125 @@
+# Generated by Django 4.1.5 on 2023-03-05 07:16
+
+from django.db import migrations, models
+import django_extensions.db.fields
+import uuid
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = []
+
+    operations = [
+        migrations.CreateModel(
+            name="VideoGame",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                (
+                    "created",
+                    django_extensions.db.fields.CreationDateTimeField(
+                        auto_now_add=True, verbose_name="created"
+                    ),
+                ),
+                (
+                    "modified",
+                    django_extensions.db.fields.ModificationDateTimeField(
+                        auto_now=True, verbose_name="modified"
+                    ),
+                ),
+                (
+                    "run_time",
+                    models.CharField(blank=True, max_length=8, null=True),
+                ),
+                (
+                    "run_time_ticks",
+                    models.PositiveBigIntegerField(blank=True, null=True),
+                ),
+                ("title", models.CharField(max_length=255)),
+                ("igdb_id", models.IntegerField(blank=True, null=True)),
+                ("alternative_name", models.CharField(max_length=255)),
+                (
+                    "uuid",
+                    models.UUIDField(
+                        blank=True,
+                        default=uuid.uuid4,
+                        editable=False,
+                        null=True,
+                    ),
+                ),
+                (
+                    "cover",
+                    models.ImageField(
+                        blank=True, null=True, upload_to="games/covers/"
+                    ),
+                ),
+                (
+                    "screenshot",
+                    models.ImageField(
+                        blank=True, null=True, upload_to="games/covers/"
+                    ),
+                ),
+                ("rating", models.FloatField(blank=True, null=True)),
+                ("rating_count", models.IntegerField(blank=True, null=True)),
+            ],
+            options={
+                "abstract": False,
+            },
+        ),
+        migrations.CreateModel(
+            name="VideoGameCollection",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                (
+                    "created",
+                    django_extensions.db.fields.CreationDateTimeField(
+                        auto_now_add=True, verbose_name="created"
+                    ),
+                ),
+                (
+                    "modified",
+                    django_extensions.db.fields.ModificationDateTimeField(
+                        auto_now=True, verbose_name="modified"
+                    ),
+                ),
+                ("name", models.CharField(max_length=255)),
+                (
+                    "uuid",
+                    models.UUIDField(
+                        blank=True,
+                        default=uuid.uuid4,
+                        editable=False,
+                        null=True,
+                    ),
+                ),
+                (
+                    "cover",
+                    models.ImageField(
+                        blank=True, null=True, upload_to="games/series-covers/"
+                    ),
+                ),
+                ("igdb_id", models.IntegerField(blank=True, null=True)),
+            ],
+            options={
+                "get_latest_by": "modified",
+                "abstract": False,
+            },
+        ),
+    ]

+ 18 - 0
vrobbler/apps/videogames/migrations/0002_videogame_release_date.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.5 on 2023-03-05 07:27
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("videogames", "0001_initial"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="videogame",
+            name="release_date",
+            field=models.DateTimeField(blank=True, null=True),
+        ),
+    ]

+ 0 - 0
vrobbler/apps/videogames/migrations/__init__.py


+ 53 - 0
vrobbler/apps/videogames/models.py

@@ -0,0 +1,53 @@
+import logging
+from typing import Dict
+from uuid import uuid4
+
+from django.conf import settings
+from django.db import models
+from django.urls import reverse
+from django_extensions.db.models import TimeStampedModel
+from scrobbles.mixins import ScrobblableMixin
+
+logger = logging.getLogger(__name__)
+BNULL = {"blank": True, "null": True}
+
+
+class VideoGameCollection(TimeStampedModel):
+    name = models.CharField(max_length=255)
+    uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
+    cover = models.ImageField(upload_to="games/series-covers/", **BNULL)
+    igdb_id = models.IntegerField(**BNULL)
+
+    def __str__(self):
+        return self.name
+
+    def get_absolute_url(self):
+        return reverse(
+            "videogames:videogamecollection_detail", kwargs={"slug": self.uuid}
+        )
+
+
+class VideoGame(ScrobblableMixin):
+    COMPLETION_PERCENT = getattr(settings, "GAME_COMPLETION_PERCENT", 100)
+
+    title = models.CharField(max_length=255)
+    igdb_id = models.IntegerField(**BNULL)
+    alternative_name = models.CharField(max_length=255)
+    uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
+    cover = models.ImageField(upload_to="games/covers/", **BNULL)
+    screenshot = models.ImageField(upload_to="games/covers/", **BNULL)
+    rating = models.FloatField(**BNULL)
+    rating_count = models.IntegerField(**BNULL)
+    release_date = models.DateTimeField(**BNULL)
+
+    def __str__(self):
+        return self.title
+
+    def get_absolute_url(self):
+        return reverse(
+            "videogames:videogame_detail", kwargs={"slug": self.uuid}
+        )
+
+    @classmethod
+    def find_or_create(cls, data_dict: Dict) -> "VideoGame":
+        ...

+ 42 - 0
vrobbler/apps/videogames/utils.py

@@ -0,0 +1,42 @@
+import logging
+from tempfile import NamedTemporaryFile
+from urllib.request import urlopen
+
+from django.core.files.base import File
+
+from videogames.models import VideoGame
+
+from vrobbler.apps.videogames.igdb import lookup_game_from_igdb
+
+logger = logging.getLogger(__name__)
+
+
+def get_or_create_videogame(
+    client_id: str, token: str, igdb_id: str
+) -> VideoGame:
+    game = None
+    logger.debug(f"Looking up video game {igdb_id}")
+    game_dict = lookup_game_from_igdb(client_id, token, igdb_id)
+
+    game = VideoGame.objects.filter(igdb_id=igdb_id).first()
+    if not game:
+        screenshot_url = game_dict.pop("screenshot_url")
+        cover_url = game_dict.pop("cover_url")
+
+        game = VideoGame.objects.create(**game_dict)
+
+        img_temp = NamedTemporaryFile(delete=True)
+        img_temp.write(urlopen(screenshot_url).read())
+        img_temp.flush()
+        img_filename = f"{game.title}_{game.uuid}.jpg"
+        game.screenshot.save(img_filename, File(img_temp))
+
+        img_temp = NamedTemporaryFile(delete=True)
+        img_temp.write(urlopen(cover_url).read())
+        img_temp.flush()
+        img_filename = f"{game.title}_{game.uuid}.jpg"
+        game.cover.save(img_filename, File(img_temp))
+
+        logger.debug(f"Created video game {game.title} ({game.igdb_id}) ")
+
+    return game

+ 3 - 2
vrobbler/settings.py

@@ -98,6 +98,7 @@ INSTALLED_APPS = [
     "podcasts",
     "sports",
     "books",
+    "videogames",
     "mathfilters",
     "rest_framework",
     "allauth",
@@ -304,11 +305,11 @@ LOGGING = {
     "loggers": {
         # Quiet down our console a little
         "django": {
-            "handlers": ["file"],
+            "handlers": ["console"],
             "propagate": True,
         },
         "django.db.backends": {"handlers": ["null"]},
-        "django.server": {"handlers": ["null"]},
+        "django.server": {"handlers": ["console"]},
         "pylast": {"handlers": ["null"], "propagate": False},
         "musicbrainzngs": {"handlers": ["null"], "propagate": False},
         "httpx": {"handlers": ["null"], "propagate": False},